Python基础学习笔记

Python貌似有点火热,上手还是比较简单的,自己找了个教程也偷偷的学习一下,扒了一下网上的图片和数据,感觉并不是很难呀(不过之前换电脑,代码丢了,有点可惜,不过网上教程一抓一大把,随便看看也能扒一些基础的图片了),所以就有了这么一篇基础的学习笔记,自己记录一下,方便自己以后要用的话很快的能够捡起来。

编译型语言

通过编译器,统一编译,一次性执行,最终生成可执行文件(相对执行更快)

代表语言:C语言

解释型语言

逐行解释每一行代码,逐行编译,逐行执行(跨平台运行能力更强),在不同的操作系统上安装不同的解释器,相同源代码使用不同的解释器进行工作

代表语言:Python

优缺点

优点

  • 简单,易学
  • 可读性强
  • 开发速度快
  • 面向对象
  • 免费,开源
  • 可扩展性
  • 具有丰富的库

缺点

  • 运行速度相对较慢
  • 国内市场较小
  • 中文资料匮乏

设计哲学

  • 优雅(代码工整)
  • 明确
  • 简单

用一种方法,最好是只有一种方法来做一件事情

Python特点

  • 完全面向对象的语言(一切皆对象)
  • 拥有一个强大的标准库
  • Python社区提供了大量的第三方模块

Python3.0没有考虑向下的兼容

Python解释器

  • CPython -- 官方版本的C语言实现
  • Jython -- 可以运行在Java平台
  • IronPython -- .NET和Mono平台
  • PyPy -- Python实现的,支持JIT即时编译

Python变量

1.不用事先声明变量,赋值过程中就包含了变量的声明和定义的过程

2.用"=" 赋值,左边是变量名,右边是变量值

3.使用前必须先被初始化(先被赋值)

4.可以通过del语句删除不再使用的变量

标识符

  • 区分大小写
  • 第一个字符必须是字母或者下划线
  • 不能使用关键字
  • 以双划线开头或者结尾的名称通常有特殊意义,不建议使用

模块和包名 -> 全小写,尽量简单

函数名 -> 全小写,多个单词用下划线隔开

类名 -> 首字母大小,采用驼峰命名法

常量名 -> 全大写,多个单词使用下划线隔开

链式赋值

使用同一个对象赋值给多个变量 x = y = 123

系列解包赋值

系列数据赋值给对应相同个数的变量(个数必须保持一致)
a,b,c=4,5,6

常量

Python不支持常量,没有语法规则限制改变一个常量的值。只能约定常量的命名规则,以及在程序的逻辑上不对常量的值做出修改

数字

// 整数除法 7//2 -> 3

** 幂 2**3 -> 8

divmod()函数同事得到商和余数 :
divmod(13,3) ->(4,1) 返回元组

整数

int_var = 1

0b或者0B -> 二进制

0o或者0O -> 八进制

0x或者0X -> 十六进制

自动转型,整数和浮点数相加 自动转化成浮点数

Python3中,int可以存储任意大小的整数,long 被取消

使用int()实现类型转换

1.浮点数直接舍去小数部分。 int(9.9) -> 9

2.布尔值True转为1,False转为0。 int(True) -> 1

3.字符串符合整数格式(浮点数据格式不行)则转换成对应整数,否则报错

时间的表示

1970年1月1日0点开始,以毫秒计算

time.time()获得当前时刻,返回的是以秒为单位精度的浮点值

浮点数

float_var = 1.0

round(value)可以返回四舍五入的值,但不会改变原有值,产生新的值

布尔型

True False

字符串型

字符串

字符串是不可变的 没有字符类型,单个字符也是字符串

Python3直接使用Unicode,可以表示世界上任何书名语言的字符

ord('苏') -> 获取单个字符的Unicode编码。chr(65) 获取Unicode编码表示的字符

Python允许空字符串的存在,不包含任何字符且长度为0

字符串切片slice操作

str = "I Love Python"
print(str[2:5]) // 输出第三个到第五个之间的字符,不包括第六个
print(str[:5])  // 输出0-5之间的字符串 (不包括第六个)
print(str[2:])  // 输出第3-最后一个的字符串(包括最后一个)
print(str * 2)  // 输出字符串两次
print(str + " And Java")    // 拼接之后输出
print(str[0:5:2])   // 最后一个代表步长step
print(str[-6])  // 倒数6个 Python
print(str[::-1])    // 步长为负,从右到左反向提取

字符串搜索

str.isalnum()   // 所有字符全是字母或者数字
str.count("Love")   // 指定字符出现的次数
str.find("L")   // 返回首次出现的下标
str.rfind("o")  // 反向搜索,返回首次出现的下标

去除字符串信息

str.strip("*")  // 去除首尾指定信息
str.lstrip("*") // 去除字符串首部左边指定信息
str.rstrip("*") // 去除字符串尾部右边指定信息

大小写转换

str.capitalize()    // 产生新的字符串,首字母大写
str.title() // 产生新的字符串,每个单词首字母大写
str.upper() // 产生新的字符串  ,所有字符串转换成大写
str.lower() // 产生新的字符串  ,所有字符串转换成小写
str.swapcase()  // 产生新的字符串  ,所有字母大小写转换

排版格式

aa = "str"
print(aa.center(10, "*"))   ->***str****
print(aa.ljust(10, "*"))    ->str*******
print(aa.rjust(10, "*"))    ->*******str

format格式化

aa = "名字是:{0},年龄是:{1}。{0}是个好小伙"
print(aa.format("Steven", 18))
b = "名字是:{name},年龄是:{age}。{name}是个好小伙"
print(b.format(name="Steven", age=18))  // 名字是:Steven,年龄是:18。Steven是个好小伙

填充和对齐

填充和对齐一起使用

^、<、>分别是居中、左对齐、右对齐。后面带宽度

:号后面带填充的字符,只能是一个字符,不指定的话默认是空格填充

a = "我的电话号码是{0:*^11}"
print(a.format(888)) // 我的电话号码是****888****

数字格式化

浮点数通过f,整数通过d进行需要的格式化

    数字              格式       输出         描述
    3.1415926       {:.2f}      3.14        保留小数点后两位
    3.1415926       {:+.2f}     3.14        带符号保留小数点后两位
    2.71828         {:.0f}      3           不带小数
    5               {:0>2d}     05          数字补零 (填充左边, 宽度为 2)
    5               {:x<4d}     5xxx        数字补 x (填充右边, 宽度为 4)
    10              {:x<4d}     10xx        数字补 x (填充右边, 宽度为 4)
    1000000         {:,}        1,000,000   以逗号分隔的数字格式
    0.25            {:.2%}      25.00%      百分比格式
    1000000000      {:.2e}      1.00E+09    指数记法
    13              {:10d}      13          右对齐 (默认, 宽度为 10)
    13              {:<10d}     13          左对齐 (宽度为 10)
    13              {:^10d}     13          中间对齐 (宽度为 10)

字符串拼接

  1. +两边都是字符串,则拼接
  2. +两边都是数字,则加法运算
  3. +两边类型不同,则抛异常

不换行打印

print("字符串",end = "*") 打印时不会换行,换行符替换成了*符号,使用*连接。*可以是任意字符串

str()实现数字转型字符串

replace()会生成一个新的字符串对象

join字符串拼接

"".join(列表) ->使用将列表中的字符串连接起来

可变字符串 io.StringIo(s)

修改过程中不会产生新的字符串对象

列表

类似于Java中的数组,是一个有序可变的集合容器,支持内置的基础数据结构甚至是列表,列表是可以嵌套的。不同的数据结构可以放在同一个列表中,没有统一类型的限制

list_a = ["str", 1, 1.0]

元组

不可变的列表,在赋值之后就不能二次更改了

tuple_a = ("str", 1, ["a", "b", 1], 2.0)
输出结果:  ('str', 1, ['a', 'b', 1], 2.0)

字典(用的比较多)

类似于Java中的map,key-value键值对的形式 无序的容器

dict_a = {
    "name": "Steven",
    "age": 27,
    1: "1800000000"
}

print(dict_a)           // {'name': 'Steven', 'age': 27, 1: '1800000000'}   
print(dict_a["name"])   // Steven
print(dict_a[1])        // 1800000000
print("name" in dict_a) 判断是否包含改键值// True
print(dict_a.keys())    // dict_keys(['name', 'age', 1])
print(dict_a.values())  // dict_values(['Steven', 27, '1800000000'])
print(dict_a.items())   // dict_items([('name', 'Steven'), ('age', 27), (1, '1800000000')])

逻辑运算符

  • and 与关系 类似于Java中的&& x and y ->x 为true,则返回y的值。x为false,则直接返回false
  • or 或关系 类似于Java中的|| x or y ->x 为true,则直接返回true。x为false,则返回y的值
  • not 非 类似于Java中的!

同一运算符is(is not)

用于比较两个对象的存储单元,实际比较的是对象的地址

整数缓存问题

Python仅仅在命令行中执行时对较小的整数对象进行缓存,范围为[-5,256]之内的整数缓存起来。而在Pycharm或者保存为文件执行时,缓存范围是[-5,任意正整数]

if语句

if (a == b):
    print("a == b")
else:
    print("a != b")

turtle图形绘制库

\行连接符 续行符

变量位于:堆内存

对象位于:栈内存

序列

序列是一种数据存储方式,用来存储一系列的数据。在内存中,序列就是一块用来存放多个值的连续的内存空间

常用的序列结构:字符串、列表、元组、字典、集合

列表:用于存储任意数目、任意类型的数据集合。

列表对象的常用方法汇总如下:

方法                          要点          描述
list.append(x)              增加元素        将元素 x 增加到列表 list 尾部
list.extend(aList)          增加元素        将列表 alist 所有元素加到列表 list 尾部
list.insert(index,x)        增加元素        在列表 list 指定位置 index 处插入元素 x
list.remove(x)              删除元素        在列表 list 中删除首次出现的指定元素 x
list.pop([index])           删除元素        删除并返回列表 list 指定位置 index 处的元素,默认是最后一个元素
list.clear()                删除所有元素      删除列表所有元素,并不是删除列表对象
list.index(x)               访问元素        返回第一个 x 的索引位置,若不存在 x 元素抛出异常
list.count(x)               计数          返回指定元素 x 在列表 list 中出现的次数
len(list)                   列表长度        返回列表中包含元素的个数
list.reverse()              翻转列表        所有元素原地翻转
list.sort()                 排序          所有元素原地排序
list.copy()                 浅拷贝             返回列表对象的浅拷贝

Python的列表大小可变,根据需要随时增加或缩小

列表的创建

基本语法[ ]创建

a = [10,20,'gaoqi','sxt']
a = [] #创建一个空的列表对象

list()创建

使用 list()可以将任何可迭代的数据转化成列表。

a = list() #创建一个空的列表对象
a = list(range(10))
a - > [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = list("gaoqi,sxt")
a - > ['g', 'a', 'o', 'q', 'i', ',', 's', 'x', 't']

range()创建整数列表

range()可以帮助我们非常方便的创建整数列表,这在开发中及其有用。语法格式为:

range([start,] end [,step])

start 参数:可选,表示起始数字。默认是 0

end 参数:必选,表示结尾数字。

step 参数:可选,表示步长,默认为 1

python3 中 range()返回的是一个 range 对象,而不是列表。我们需要通过 list()方法将其转换成列表对象

a = [x*2 for x in range(100) if x%9==0] #通过 if 过滤元素
[0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198]

列表元素的增加和删除

当列表增加和删除元素时,列表会自动进行内存管理,大大减少了程序员的负担。但这个特点涉及列表元素的大量移动,效率较低。除非必要,我们一般只在列表的尾部添加元素或删除元素,这会大大提高列表的操作效率。

append()方法

原地修改列表对象,是真正的列表尾部添加新的元素,速度最快,推荐使用。

a = [20,40]
a.append(80)

+运算符操作

并不是真正的尾部添加元素,而是创建新的列表对象;将原列表的元素和新列表的元素依次复制到新的列表对象中。这样,会涉及大量的复制操作,对于操作大量元素不建议使用。

a = a+[50]

extend()方法

将目标列表的所有元素添加到本列表的尾部,属于原地操作,不创建新的列表对象。
a.extend([50,60])

insert()插入元素

使用 insert()方法可以将指定的元素插入到列表对象的任意制定位置。这样会让插入位置后
面所有的元素进行移动,会影响处理速度。涉及大量元素时,尽量避免使用。

类似发生这种移动的函数还有:remove()、pop()、del(),它们在删除非尾部元素时也会发生操作位置后面元素的移动。
a.insert(2,100)

列表元素的删除

本质上是列表的拷贝,将后面的赋值拷贝到前面的值中

del 删除

 del a[1]

pop()方法

pop()删除并返回指定位置元素,如果未指定位置则默认操作列表最后一个元素。
a = [10, 20, 30, 40]
a.pop() -> 返回 40
a.pop(1) -> 返回 20

remove()方法

删除首次出现的指定元素,若不存在该元素抛出异常。
a = [10,20,30,40,50,20,30,20,30]
a.remove(20)

列表元素访问和计数

通过索引直接访问元素

我们可以通过索引直接访问元素。索引的区间在[0, 列表长度-1]这个范围。超过这个范围则会抛出异常。

index()获得指定元素在列表中首次出现的索引

index()可以获取指定元素首次出现的索引位置。语法是:index(value,[start,[end]])。其中,
start 和 end 指定了搜索的范围。

count()获得指定元素在列表中出现的次数

len()返回列表长度

成员资格判断

判断列表中是否存在指定的元素,我们可以使用 count()方法,返回 0 则表示不存在,返回大于 0 则表示存在。但是,一般我们会使用更加简洁的 in 关键字来判断,直接返回 True或 False。

切片操作

切片是 Python 序列及其重要的操作,适用于列表、元组、字符串等等

切片 slice 操作可以让我们快速提取子列表或修改。标准格式为:
[起始偏移量 start:终止偏移量 end[:步长 step]]

操作和说明                                   示例                      结果
[:] 提取整个列表                           [10,20,30][:]          [10,20,30]
[start:]从 start 索引开始到结尾            [10,20,30][1:]         [20,30]
[:end]从头开始直到 end-1                  [10,20,30][:2]         [10,20]
[start:end]从start到 end-1              [10,20,30,40][1:3]     [20,30]
[start:end:step] 从 start 提取到 end-1,步长是 step         [10,20,30,40,50,60,70][1:6:2]       [20, 40, 60]

其他操作(三个量为负数)的情况:

示例                              说明                                  结果
[10,20,30,40,50,60,70][-3:]     倒数三个                               [50,60,70]
10,20,30,40,50,60,70][-5:-3]    倒数第五个到倒数第三个(包头不包尾)        [30,40]
[10,20,30,40,50,60,70][::-1]    步长为负,从右到左反向提取               [70, 60, 50, 40, 30, 20, 10]

切片操作时,起始偏移量和终止偏移量不在[0,字符串长度-1]这个范围,也不会报错。起始
偏移量小于 0 则会当做 0,终止偏移量大于“长度-1”会被当成”长度-1”

列表的遍历

for obj in listObj:
    print(obj)

列表排序

修改原列表,不建新列表的排序

 a.sort() #默认是升序排列
 a.sort(reverse=True) #降序排列
 random.shuffle(a) #打乱顺序

建新列表的排序

通过内置函数 sorted()进行排序,这个方法返回新列表,不对原列表做修改。

a = sorted(a) #默认升序
c = sorted(a,reverse=True) #降序

reversed()返回迭代器

内置函数 reversed()也支持进行逆序排列,与列表对象 reverse()方法不同的是,内置函数
reversed()不对原列表做任何修改,只是返回一个逆序排列的迭代器对象。
迭代器只能使用一次 第二次会返回空

a = [20,10,30,40]
c = reversed(a)

max 和 min

用于返回列表中最大和最小值。

max(a)
min(a)

sum

对数值型列表的所有元素进行求和操作,对非数值型列表运算则会报错。

sum(a)

多维列表

二维列表

一维列表可以帮助我们存储一维、线性的数据。

二维列表可以帮助我们存储二维、表格的数据。

a = [
    ["高小一",18,30000,"北京"],
    ["高小二",19,20000,"上海"],
    ["高小一",20,10000,"深圳"],
]
 print(a[1][0],a[1][1],a[1][2])

元组 tuple

列表属于可变序列,可以任意修改列表中的元素。元组属于不可变序列,不能修改元组中的元素。因此,元组没有增加元素、修改元素、删除元素相关的方法。
元组支持如下操作:

1. 索引访问
2. 切片操作
3. 连接操作
4. 成员关系操作
5. 比较运算操作
6. 计数:元组长度 len()、最大值 max()、最小值 min()、求和 sum()等。
  1. 通过()创建元组。小括号可以省略。

     a = (10,20,30) 或者 a = 10,20,30
    

如果元组只有一个元素,则必须后面加逗号。这是因为解释器会把(1)解释为整数 1,(1,)解释为元组。

  1. 通过 tuple()创建元组
    tuple(可迭代的对象)

     b = tuple() #创建一个空元组对象
     b = tuple("abc")
     b = tuple(range(3))
     b = tuple([2,3,4])
    

tuple()可以接收列表、字符串、其他序列类型、迭代器等生成元组。

list()可以接收元组、字符串、其他序列类型、迭代器等生成列表。

元组的元素访问和计数

  1. 元组的元素不能修改

  2. 元组的元素访问和列表一样,只不过返回的仍然是元组对象。

     a = (20,10,30,9,8)
     a[1]    -> 10
     a[1:3]  -> (10, 30) 
     a[:4]   -> (20, 10, 30, 9)
    
  3. 列表关于排序的方法 list.sort()是修改原列表对象,元组没有该方法。如果要对元组排序,只能使用内置函数 sorted(tupleObj),并生成新的列表对象。

     a = (20,10,30,9,8)
     sorted(a)   -> [8, 9, 10, 20, 30]
    

zip

zip(列表 1,列表 2,...)将多个列表对应位置的元素组合成为元组,并返回这个 zip 对象

a = [10,20,30]
b = [40,50,60]
c = [70,80,90]
d = zip(a,b,c)
list(d) -> [(10, 40, 70), (20, 50, 80), (30, 60, 90)]

生成器推导式创建元组

从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。列表推
导式直接生成列表对象,生成器推导式生成的不是列表也不是元组,而是一个生成器对象。

我们可以通过生成器对象,转化成列表或者元组。也可以使用生成器对象的__next__()
方法进行遍历,或者直接作为迭代器对象来使用。不管什么方式使用,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该生成器对象。

s = (x * 2 for x in range(5))
print(tuple(s)) -> (0, 2, 4, 6, 8) #只能访问一次元素。第二次就为空了。需要再生成一次

元组总结

  1. 元组的核心特点是:不可变序列。
  2. 元组的访问和处理速度比列表快。
  3. 与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用。

字典

字典是“键值对”的无序可变序列,字典中的每个元素都是一个“键值对”,包含:“键
对象”和“值对象”。可以通过“键对象”实现快速获取、删除、更新对应的“值对象”。

键”是任意的不可变数据,比如:整数、浮点数、字符串、元组。但是:列表、
字典、集合这些可变对象,不能作为“键”。并且“键”不可重复。

字典的创建

  1. 我们可以通过{}、dict()来创建字典对象。

     a = {'name':'Steven','age':18,'job':'programmer'}
     b = dict(name='Steven',age=18,job='programmer')
     a = dict([("name","Steven"),("age",18)])
     a = dict({("name", "Steven"), ("age", 18)})
     a = dict({'name': 'Steven', 'age': 18, 'job': 'programmer'})
     c = {} #空的字典对象
     d = dict() #空的字典对象
    
  2. 通过 zip()创建字典对象

     k = ['name','age','job']
     v = ['Steven',18,'programmer']
     d = dict(zip(k,v))
     print(d)    -> {'name': 'Steven', 'age': 18, 'job':'programmer'}
    
  3. 通过 fromkeys 创建值为空的字典

     a = dict.fromkeys(['name','age','job'])
     print(a)    -> {'name': None, 'age': None, 'job': None} # none 是一个值,表示没有赋值
    

字典元素的访问

a = {'name':'Steven','age':18,'job':'programmer'}
  1. 通过 [键] 获得“值”。若键不存在,则抛出异常。

     a['name']   -> 'Steven'
    
  2. 通过 get()方法获得“值”。推荐使用。优点是:指定键不存在,返回 None;也可以设定指定键不存在时默认返回的对象。推荐使用 get()获取“值对象”。

     a.get('name')   -> 'Steven'
     a.get('sex','一个男人') -> '一个男人'
    
  3. 列出所有的键值对

     a.items()   -> dict_items([('name', 'Steven'), ('age', 18), ('job', 'programmer')])
    
  4. 列出所有的键,列出所有的值

     a.keys()    -> dict_keys(['name', 'age', 'job'])
     a.values()  -> dict_values(['Steven', 18, 'programmer'])
    
  5. len() 键值对的个数

     len(a)
     a.__len__()
    
  6. 检测一个“键”是否在字典中

     print("name" in a)  -> True
    

字典元素添加、修改、删除

  1. 给字典新增“键值对”。如果“键”已经存在,则覆盖旧的键值对;如果“键”不存在,
    则新增“键值对”。

     a['address']='世界大厦1101'
     a['age']=16
     print(a)    -> {'name': 'Steven', 'age': 16, 'job': 'programmer', 'address': '世界大厦1101'}
    
  2. 使用 update()将新字典中所有键值对全部添加到旧字典对象上。如果 key 有重复,则直接覆盖

     b = {'name': 'StevenSon', 'money': 1000, 'sex': '男的'}
     a.update(b)
     print(a)    -> {'name': 'StevenSon', 'age': 16, 'job': 'programmer', 'money': 1000, 'sex': '男的'}
    
  3. 字典中元素的删除,可以使用 del()方法;或者 clear()删除所有键值对;pop()删除指定键值对,并返回对应的“值对象”;

     del(a["name"])
     print(a)    -> {'age': 18, 'job': 'programmer'}
     a_value = a.pop("age")
     print(a)    -> {'job': 'programmer'}
     print(a_value)  -> 18
    
  4. popitem() :随机删除和返回该键值对。若想一个接一个地移除并处理项,这个方法就非常有效(因为不用首先获取键的列表)。

     print(a.popitem())  -> ('job', 'programmer')
     print(a)    -> {'name': 'Steven', 'age': 18}
    

序列解包

序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。

 x,y,z=(20,30,10)
(a,b,c)=(9,8,10)
 [a,b,c]=[10,20,30]

序列解包用于字典时,默认是对“键”进行操作; 如果需要对键值对操作,则需要使用
items();如果需要对“值”进行操作,则需要使用 values();

s = {'name':'gaoqi','age':18,'job':'teacher'}
name,age,job=s #默认对键进行操作
name,age,job=s.items() #对键值对进行操作
name,age,job=s.values() #对值进行操作

字典核心底层原理(重要)

字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的
每个单元叫做 bucket。每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用。

由于,所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。

Python基础学习笔记_第1张图片
字典底层原理.png
Python基础学习笔记_第2张图片
字典底层原理2.png

根据键查找“键值对”的底层过程

Python基础学习笔记_第3张图片
字典底层取值.png

用法总结:

  1. 键必须可散列
    (1) 数字、字符串、元组,都是可散列的。
    (2) 自定义对象需要支持下面三点:

     1 支持 hash()函数
     2 支持通过__eq__()方法检测相等性。
     3 若 a==b 为真,则 hash(a)==hash(b)也为真。
    
  2. 字典在内存中开销巨大,典型的空间换时间。

  3. 键查询速度很快

  4. 往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。 会报RuntimeError: dictionary changed size during iteration错误

集合

集合是无序可变,元素不能重复。实际上,集合底层是字典实现,集合的所有元素都是字典中的“键对象”,因此是不能重复的且唯一的。

集合创建和删除

  1. 使用{}创建集合对象,并使用 add()方法添加元素

     a = {3, 5, 7}
     a.add(9)
    
  2. 使用 set(),将列表、元组等可迭代对象转成集合。如果原来数据存在重复数据,则只保留一个。

    b = set(a)

  3. remove()删除指定元素;clear()清空整个集合

    a.remove(3)

集合相关操作

Python 对集合提供了并集、交集、差集等运算

a = {1, 3, 'sxt'}
b = {'he', 'it', 'sxt'}
a|b 或者  a.union(b)  #并集 ->  {1, 3, 'sxt', 'he', 'it'}
a&b 或者  a.intersection(b)   #交集 ->{'sxt'}
a-b 或者  a.difference(b) #差集 ->{1, 3}

控制语句

选择结构

单分支选择结构

if 语句单分支结构的语法形式如下:

if 条件表达式:
    语句/语句块

num = input("输入一个数字:")
if int(num)<10:
    print(num)

条件表达式详解

在选择和循环结构中,条件表达式的值为 False 的情况如下:

False、0、0.0、空值 None、空序列对象(空列表、空元祖、空集合、空字典、空字符串)、空 range 对象、空迭代对象。其他情况均为True

条件表达式中,不能有赋值操作符“=”

双分支选择结构

双分支结构的语法格式如下:

if 条件表达式 :
    语句 1/语句块 1
else:
    语句 2/语句块 2

三元条件运算符

Python 提供了三元运算符,用来在某些简单双分支赋值情况。三元条件运算符语法格式如下:

条件为真时的值 if (条件表达式) else 条件为假时的值

多分支选择结构

多分支选择结构的语法格式如下:

if 条件表达式 1 :
    语句 1/语句块 1
elif 条件表达式 2:
    语句 2/语句块 2
.
.
.
elif 条件表达式 n :
    语句 n/语句块 n
[else:
    语句 n+1/语句块 n+1
]

【注】计算机行业,描述语法格式时,使用中括号[ ]通常表示可选,非必选。

选择结构嵌套

选择结构可以嵌套,使用时一定要注意控制好不同级别代码块的缩进量,因为缩进量决定了代码的从属关系。

循环结构

循环结构用来重复执行一条或多条语句。表达这样的逻辑:如果符合条件,则反复执行循环体里的语句。在每次执行完后都会判断一次条件是否为 True,如果为 True 则重复执行循环体里的语句。

循环体里面的语句至少应该包含改变条件表达式的语句,以使循环趋于结束;否则,就会变成一个死循环。

while 循环

while 循环的语法格式如下:

while 条件表达式:
    循环体语句

for 循环和可迭代对象遍历

for 循环通常用于可迭代对象的遍历。for 循环的语法格式如下:

for 变量 in 可迭代对象:
    循环体语句

可迭代对象

Python 包含以下几种可迭代对象:

  1. 序列。包含:字符串、列表、元组
  2. 字典
  3. 迭代器对象(iterator)
  4. 生成器函数(generator)
  5. 文件对象

range 对象

range 对象是一个迭代器对象,用来产生指定范围的数字序列。格式为:

range(start, end [,step])

生成的数值序列从 start 开始到 end 结束(不包含 end)。若没有填写 start,则默认从 0开始。step 是可选的步长,默认为 1。

for i in range(10) 产生序列:0 1 2 3 4 5 6 7 8 9
for i in range(3,10) 产生序列:3 4 5 6 7 8 9
for i in range(3,10,2) 产生序列:3 5 7 9

break 语句

break 语句可用于 while 和 for 循环,用来结束整个循环。当有嵌套循环时,break 语句只能跳出最近一层的循环。

continue 语句

continue 语句用于结束本次循环,继续下一次。多个循环嵌套时,continue 也是应用于最
近的一层循环。

else 语句

while、for 循环可以附带一个 else 语句(可选)。如果 for、while 语句没有被 break 语句
结束,则会执行 else 子句,否则不执行。语法格式如下:

while 条件表达式:
    循环体
else:
    语句块

或者:
for 变量 in 可迭代对象:
    循环体
else:
    语句块

循环代码优化

编写循环时,遵守下面三个原则可以大大提高运行效率,避免不必要的低效计算:

  1. 尽量减少循环内部不必要的计算
  2. 嵌套循环中,尽量减少内层循环的计算,尽可能向外提。
  3. 局部变量查询较快,尽量使用局部变量

其他优化手段

  1. 连接多个字符串,使用 join()而不使用+
  2. 列表进行元素插入和删除,尽量在列表尾部操作

使用 zip()并行迭代

我们可以通过 zip()函数对多个序列进行并行迭代,zip()函数在最短序列“用完”时就会停止

names = ("Steven","高老二","高老三","高老四")
ages = (18,16,20,25)
jobs = ("老师","程序员","公务员")
for name,age,job in zip(names,ages,jobs):
print("{0}--{1}--{2}".format(name,age,job)

执行结果:
Steven--18--老师
高老二--16--程序员
高老三--20--公务员

推导式创建序列

推导式是从一个或者多个迭代器快速创建序列的一种方法。它可以将循环和条件判断结合,
从而避免冗长的代码。推导式是典型的 Python 风格,会使用它代表你已经超过 Python 初
学者的水平。

列表推导式

列表推导式生成列表对象,语法如下:

[表达式 for item in 可迭代对象 ]
或者:
{表达式 for item in 可迭代对象 if 条件判断
 [x*2 for x in range(1,5)] -> [2, 4, 6, 8]
 [x*2 for x in range(1,20) if x5==0 ] -> [10, 20, 30]
 [a for a in "abcdefg"] -> ['a', 'b', 'c', 'd', 'e', 'f', 'g']
 cells = [(row,col) for row in range(1,10) for col in range(1,10)] #可以使用两个循环

字典推导式

字典的推导式生成字典对象,格式如下:

{key_expression : value_expression for 表达式 in 可迭代对象}

 my_text = ' i love you, i love sxt,i love Steven'
char_count = {c:my_text.count(c) for c in my_text}

集合推导式

集合推导式生成集合,和列表推导式的语法格式类似:

{表达式 for item in 可迭代对象 }
或者:
{表达式 for item in 可迭代对象 if 条件判断}

生成器推导式(生成元组)

元组是没有推导式的,推导式推导出的是一个生成器,可以通过生成器生成元组。一个生成器只能运行一次。第一次迭代可以得到数据,第二次迭代发现数据已经没有了。

tunple((x for x in range(4))) -> (0, 1, 2, 3)

函数用法和底层分析

Python 函数的分类

Python 中函数分为如下几类:

  1. 内置函数
    我们前面使用的 str()、list()、len()等这些都是内置函数,我们可以拿来直接使用。
  2. 标准库函数
    我们可以通过 import 语句导入库,然后使用其中定义的函数
  3. 第三方库函数
    Python 社区也提供了很多高质量的库。下载安装这些库后,也是通过 import 语句导
    入,然后可以使用这些第三方库的函数
  4. 用户自定义函数

函数的定义和调用

核心要点

Python 中,定义函数的语法如下:

def 函数名 ([参数列表]) :
    '''文档字符串'''
    函数体/若干语句
  1. Python 执行 def 时,会创建一个函数对象,并绑定到函数名变量上。即函数也是一个对象

  2. 参数列表

    (1) 圆括号内是形式参数列表,有多个参数则使用逗号隔开

    (2) 形式参数不需要声明类型,也不需要指定函数返回值类型

    (3) 无参数,也必须保留空的圆括号

    (4) 实参列表必须与形参列表一一对应

  3. return 返回值

    (1) 如果函数体中包含 return 语句,则结束函数执行并返回值;

    (2) 如果函数体中不包含 return 语句,则返回 None 值。

return 返回值要点:

  1. 如果函数体中包含 return 语句,则结束函数执行并返回值;
  2. 如果函数体中不包含 return 语句,则返回 None 值。
  3. 要返回多个返回值,使用列表、元组、字典、集合将多个值“存起来”即可。

文档字符串(函数的注释)

通过三个单引号或者三个双引号来实现,中间可以加入多行文字进行说明。

调用 help(函数名.__doc__)可以打印输出函数的文档字符串

变量的作用域( 全局变量和局部变量)

全局变量:

  1. 在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块
    结束。

  2. 全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。

  3. 全局变量一般做常量使用。

  4. 函数内要改变全局变量的值,使用 global 声明一下

     global a #如果要在函数内改变全局变量的值,增加 global 关键字声明
    

局部变量:

  1. 在函数体中(包含形式参数)声明的变量。
  2. 局部变量的引用比全局变量快,优先考虑使用。
  3. 如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量

参数的传递

函数的参数传递本质上就是:从实参到形参的赋值操作。 Python 中“一切皆对象”,
所有的赋值操作都是“引用的赋值”。所以,Python 中参数的传递都是“引用传递”,不
是“值传递”。具体操作时分为两类:

  1. 对“可变对象”进行“写操作”,直接作用于原对象本身。
  2. 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填
    充这块空间。(起到其他语言的“值传递”效果,但不是“值传递”)

可变对象

字典、列表、集合、自定义的对象等

不可变对象

数字、字符串、元组、function 等

传递可变对象的引用

传递参数是可变对象(例如:列表、字典、自定义的其他可变对象等),实际传递的还是对
象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象

传递可变对象的引用

传递参数是可变对象,实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。

a = 100
def f1(n):
    print("n:",id(n)) #传递进来的是 a 对象的地址
    n = n+200 #由于 a 是不可变对象,因此创建新的对象 n
    print("n:",id(n)) #n 已经变成了新的对象
    print(n)
f1(a)
print("a:",id(a)

执行结果:
n: 1663816464
n: 46608592
300
a: 1663816464

浅拷贝和深拷贝

内置函数:copy(浅拷贝)、deepcopy(深拷贝)。

浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。

深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象

传递不可变对象包含的子对象是可变的情况

传递不可变对象时。不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象,源对象也发生了变化

参数的几种类型

位置参数

函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数,称为:“位置参数”。

默认值参数

我们可以为某些参数设置默认值,这样这些参数在传递时就是可选的。称为“默认值参数”。默认值参数放到位置参数后面。

命名参数

我们也可以按照形参的名称传递参数,称为“命名参数”,也称“关键字参数”。参数传递顺序可变

可变参数

可变参数指的是“可变数量的参数”。分两种情况:

  1. *param(一个星号),将多个参数收集到一个“元组”对象中。

  2. **param(两个星号),将多个参数收集到一个“字典”对象中。

     def f1(a,b,*c):
         print(a,b,c)
     f1(8,9,19,20)
    
     def f2(a,b,**c):
         print(a,b,c)
     f2(8,9,name='gaoqi',age=18)
    
     def f3(a,b,*c,**d):
         print(a,b,c,d)
     f3(8,9,20,30,name='gaoqi',age=18
    

强制命名参数

在带星号的“可变参数”后面增加新的参数,必须在调用的时候“强制命名参数”

lambda 表达式和匿名函数

lambda 表达式可以用来声明匿名函数。lambda 函数是一种简单的、在同一行中定义函数
的方法。lambda 函数实际生成了一个函数对象。
lambda 表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数
的返回值

lambda 表达式的基本语法如下:

lambda arg1,arg2,arg3... : <表达式>

arg1/arg2/arg3 为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。

eval()函数

功能:将字符串 str 当成有效的表达式来求值并返回计算结果。

nonlocal 关键字

nonlocal 用来声明外层的局部变量。

global 用来声明全局变量

LEGB 规则

Python 在查找“名称”时,是按照 LEGB 规则查找的:

Local-->Enclosed-->Global-->Built in

Local 指的就是函数或者类的方法内部

Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)

Global 指的是模块中的全局变量

Built in 指的是 Python 为自己保留的特殊名称。

对象

类的定义

定义类的语法格式:

class 类名:
    类体
  1. 类名必须符合“标识符”的规则;一般规定,首字母大写,多个单词使用“驼峰原则”。
  2. 类体中我们可以定义属性和方法。
  3. 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作。

__init__构造方法和__new__方法

类是抽象的,也称之为“对象的模板”。

__init__()的要点如下:

  1. 名称固定,必须为:__init__()
  2. 第一个参数固定,必须为:self。 self 指的就是刚刚创建好的实例对象。
  3. 构造函数通常用来初始化实例对象的实例属性
  4. 通过“类名(参数列表)”来调用构造函数
  5. __init__()方法:初始化创建好的对象,初始化指的是:“给实例属性赋值”
  6. __new__()方法: 用于创建对象,但我们一般无需重定义该方法。
  7. 如果我们不定义__init__()方法,系统会提供一个默认的__init__()方法。如果我们定义了带参
    的__init__()方法,系统不创建默认的__init__()方法。

注:

Python 中的 self 相当于 C++中的 self 指针,JAVA 和 C#中的 this 关键字

Python 中,self 必须为构造函数的第一个参数,名字可以任意修改。但一般遵守惯例,都叫做 self。

实例属性和实例方法

实例属性

实例属性是从属于实例对象的属性,也称为“实例变量”。

使用要点:

  1. 实例属性一般在\__init__()方法中通过如下代码定义:
    self.实例属性名 = 初始值

  2. 在本类的其他实例方法中,也是通过 self 进行访问:
    self.实例属性名

  3. 创建实例对象后,通过实例对象访问:

     obj01 = 类名() #创建对象,调用__init__()初始化属性
     obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属性
    

实例方法

实例方法是从属于实例对象的方法。实例方法的定义格式如下:

    def 方法名(self [, 形参列表]):
        函数体

方法的调用格式如下:

    对象.方法名([实参列表])

要点:

  1. 定义实例方法时,第一个参数必须为 self。和前面一样,self 指当前的实例对象。
  2. 调用实例方法时,不需要也不能给 self 传参。self 由解释器自动传参。

函数和方法的区别

  1. 都是用来完成一个功能的语句块,本质一样。
  2. 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
  3. 直观上看,方法定义时需要传递 self,函数不需要。

其他操作:

  1. dir(obj)可以获得对象的所有属性、方法
  2. obj._dict_ 对象的属性字典
  3. pass 空语句
  4. isinstance(对象,类型) 判断“对象”是不是“指定类型”

类对象、类属性、类方法、静态方法

类对象

类对象的类型就是type

pass 为空语句。就是表示什么都不做,只是作为一个占位符存在。当你写代码时,
遇到暂时不知道往方法或者类中加入什么时,可以先用 pass 占位,后期再补上。

类属性

类属性是从属于“类对象”的属性,也称为“类变量”。由于,类属性从属于类对象,可以
被所有实例对象共享。

类属性的定义方式:

class 类名:
    类变量名= 初始值


class Student:
    company = "SXT" #类属性
    count = 0 #类属性

    def __init_(self,name,score):
        self.name = name #实例属性
        self.score = scor

类方法

类方法是从属于“类对象”的方法。类方法通过装饰器@classmethod 来定义,格式如下:

@classmethod
def 类方法名(cls [,形参列表]) :
    函数体

要点如下:

  1. @classmethod 必须位于方法上面一行
  2. 第一个 cls 必须有;cls 指的就是“类对象”本身;
  3. 调用类方法格式:“类名.类方法名(参数列表)”。 参数列表中,不需要也不能给 cls 传
    值。
  4. 类方法中访问实例属性和实例方法会导致错误
  5. 子类继承父类方法时,传入 cls 是子类对象,而非父类对象

静态方法

Python 中允许定义与“类对象”无关的方法,称为“静态方法”。

“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空
间里面”,需要通过“类调用”。

静态方法通过装饰器@staticmethod 来定义,格式如下:

@staticmethod
def 静态方法名([形参列表]) :
    函数体

要点如下:

  1. @staticmethod 必须位于方法上面一行
  2. 调用静态方法格式:“类名.静态方法名(参数列表)”。
  3. 静态方法中访问实例属性和实例方法会导致错误

__del__方法(析构函数)和垃圾回收机制

__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。

__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。

可以通过 del 语句删除对象,从而保证调用__del__方法。系统会自动提供__del__方法,一般不需要自定义析构方法。

__call__方法和可调用对象

定义了__call__方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用。

class SalaryAccount:

    def __call__(self, salary):
        yearSalary = salary*12
        daySalary = salary//30
        hourSalary = daySalary//8

        return dict(monthSalary=salary,yearSalary=yearSalary,daySalary=daySalary,hourSalary=hourSalary)



s = SalaryAccount()
print(s(5000)

{'monthSalary': 5000, 'yearSalary': 60000, 'daySalary': 166, 'hourSalary': 20}

方法没有重载

Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由
可变参数控制。

不要使用重名的方法!Python 中方法没有重载

方法的动态性

Python 是动态语言,我们可以动态的为类添加新的方法,或者动态的修改类的已有的方法。

Python 一切皆对象

私有属性和私有方法(实现封装)

Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有
属性和私有方法,有如下要点:

  1. 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)。
  2. 类内部可以访问私有属性(方法)
  3. 类外部不能直接访问私有属性(方法)
  4. 类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法)p._Employee__age

@property 装饰器

@property 可以将一个方法的调用方式变成“属性调用”。

class Employee
    def __init__(self,name,salary):
        self.name = name
        self.__salary = salary
    @property #相当于 salary 属性的 getter 方法

    def salary(self):
        print("月薪为{0},年薪为{1}".format(self.__salary,(12*self.__salary)))
        return self.__salary;

    @salary.setter
    def salary(self,salary): #相当于 salary 属性的 setter 方法

        if(0

面向对象

继承

语法格式

Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:

class 子类类名(父类 1[,父类 2,...]):
    类体

默认父类是 object 类

class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age

class Student(Person):
    def __init__(self,name,age,score):
        self.score = score
        Person.__init__(self,name,age) #构造函数中包含调用父类构造函数。根据需要,不是必须。 
子类并不会自动调用父类的\_\_init\_\_(),我们必须显式的调用

类成员的继承和重写

  1. 成员继承:子类继承了父类除构造方法之外的所有成员。
  2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”

重写__str__()方法

object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数 str()
经常用于 print()方法,帮助我们查看对象的信息。__str__()可以重写。

多重继承

Python 支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是由于这样会被“类的整体层次”搞的异常复杂,尽量避免使用。

MRO()

Python 支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将
“从左向右”按顺序搜索。

MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro()方法获得
“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。

super()获得父类定义

在子类中,如果想要获得父类的方法时,我们可以通过 super()来做。

super()代表父类的定义,不是父类对象。

多态

多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。

  1. 多态是方法的多态,属性没有多态。
  2. 多态的存在有 2 个必要条件:继承、方法重写。

特殊方法和运算符重载

Python 的运算符实际上是通过调用对象的特殊方法实现的。

常见的特殊方法统计如下:

方法              说明              例子
__init__            构造方法        对象创建:p = Person()
__del__             析构方法        对象回收
__repr__,__str__    打印,转换       print(a)
__call__            函数调用        a()
__getattr__         点号运算        a.xxx
__setattr__         属性赋值        a.xxx = value
__getitem__         索引运算        a[key]
__setitem__         索引赋值        a[key]=value
__len__             长度              len(a)



运算符             特殊方法                说明
运算符+            __add__                 加法
运算符-            __sub__                 减法
<,<=,==         __lt__,__le__,__eq__    比较运算符
>,>=,!=        __gt__,__ge__,__ne__     比较运算符
|,^,&           __or__,__xor__,__and__  或、异或、与
<<,>>           __lshift__,__rshift__   左移、右移
*,/,%,//        __mul__,__truediv__,__mod__,__floordiv__    乘、浮除、模运算
(取余)、整数除
**              __pow__                 指数运算

每个运算符实际上都对应了相应的方法

特殊属性

Python 对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法

特殊方法                含义
obj.__dict__            对象的属性字典
obj.__class__           对象所属的类
class.__bases__         类的基类元组(多继承)
class.__base__          类的基类
class.__mro__           类层次结构
class.__subclasses__()  子类列表

对象的浅拷贝和深拷贝

  • 变量的赋值操作

只是形成两个变量,实际还是指向同一个对象。

  • 浅拷贝

Python 拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象
和拷贝对象会引用同一个子对象。

  • 深拷贝

使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象
所有的子对象也不同

组合

is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。

“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。

设计模式_工厂模式实现

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。

class CarFactory:
    def createCar(self,brand):
        if brand == "奔驰":
            return Benz()
        elif brand == "宝马":
            return BMW()
        elif brand == '比亚迪':
            return BYD()
        else:
            return "未知品牌,无法创建"

class Benz:
    pass
class BMW:
    pass
class BYD:
    pass

factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")

设计模式_单例模式实现

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
class MySingleton:
__obj = None
__init_flag = True

    def __new__(cls, *args, **kwargs):
        if cls.__obj ==  None:
            cls.__obj = object.__new__(cls) # 创建父类对象
        return cls.__obj

    def __init__(self,name):
        if MySingleton.__init_flag: # 保证初始化只执行一次
            print( "init....")
            self.name = name
            MySingleton.__init_flag =  False

a = MySingleton( "aa")
print(a)
b = MySingleton( "bb")
print(b)

异常

在执行程序的时候遇到错误,会停止程序的执行,并提示错误信息,这就是异常

捕获异常

语法格式:

try:
    # 尝试执行的代码
except:
    # 出现错误的处理

捕获特殊错误类型

程序运行出错的第一个单词,就是错误类型

try:
    # 尝试执行的代码
    pass
except 错误类型1:
    # 出现错误类型1的处理
    pass
except (错误类型2, 错误类型3):
    # 出现错误类型2或者错误类型3的处理
    pass

捕获未知错误

except Exception as result: # result变量接收错误信息
    print("未知错误 %s" % result)

异常捕获完整语法

try:
    pass
except ValueError:
    pass
except Exception as result:
    pass
else:
    # 没有异常执行的代码
    pass
finally:
    # 无论是否出现异常,都会执行。最后执行
    pass

异常的传递

函数/方法出现错误时,会把异常传递给函数/方法的调用处,直到主程序

当异常抛出到主程序时,仍然没有进行异常捕获,程序就会终止

利用异常的传递性,可以在主程序中捕获异常

抛出raise异常

  1. 创建一个Exception的对象

  2. 使用raise关键字抛出异常对象

     # 创建异常对象
     ex = Exception("异常描述信息")
     # 主动抛出异常
     raise ex # raise + 异常对象
    

模块

模块的导入

import导入

模块导入关键字import,导入模块应该单行导入一个模块

模块名.的方式使用模块中提供的类,全局变量,函数

使用关键字as指定模块的别名,别名命名方法使用大驼峰命名法(单个单词首字母大写)

import 模块名 as 模块名

from...import导入

从一个模块中,导入部分工具

from 模块名 import 工具名

通过from导入之后,不需要通过模块名.的方式来使用。可以直接使用工具名进行使用

注意:如果两个模块,存在同名的函数,那么后导入的函数,会覆盖掉之前导入的函数。

一旦发现有同名的函数,可以使用as指定别名

模块的搜索顺序

  1. 搜索当前目录指定模块名的文件,如果有就直接导入
  2. 如果没有,再搜索系统目录

__name__属性

文件被导入时,能够直接执行的代码不需要被执行!因此需要使用__name__属性来做判断。

__name__是Python的一个内置属性,记录着一个字符串.

如果是被其他文件导入的,__name__就是模块名

如果是当前执行的程序__name__就是__main__

Python文件的标准代码格式:

# 导入模块
# 定义全局变量
# 定义类
# 定义函数

def main():
    pass

# 根据__name__属性判断是否需要执行代码
if __name__="__main__"
    main()

  • 包是一个包含多个模块的特殊目录
  • 目录下有一个特殊文件:__init__.py
  • 包的命名方式和变量名一样,小写字母+下划线

使用import 包名 可以一次性导入包中的所有模块

__init__.py

要在外界使用包中的模块,需要在__init__.py文件中指定对外界提供的模块列表

from . import 模块列表

发布模块

将自己开发的模块,分享给其他人

制作发布压缩包

Python基础学习笔记_第4张图片
制作压缩包步骤.png

安装删除模块

Python基础学习笔记_第5张图片
安装删除模块.png

pip安装删除模块

Python基础学习笔记_第6张图片
pip安装删除模块.png

文件

文件操作步骤

  1. 打开文件 open()
  2. 读写文件 read() write()
  3. 关闭文件 close()

read()函数运行之后会将指针移动到文件末尾,再次读的话是读取不到内容的

文件复制

小文件

小文件直接复制

file = open("move.json", encoding="utf-8")
text = file.read()

copyFile = open("copyMove.json", mode="w", encoding="utf-8")
copyFile.write(text)

file.close()
copyFile.close()

大文件

大文件需要逐行复制

file = open("move.json", encoding="utf-8")
copyFile = open("copyMove.json", mode="w", encoding="utf-8")
while True:
    text = file.readline() # 逐行读取
    if not text:
        break
    copyFile.write(text)
    
file.close()
copyFile.close()

文件操作

文件操作需要导入os模块

rename() 重命名

remove() 删除文件

# 文件重命名
os.rename("copyMove.json", "reNameCopyMove.json")
# 删除文件
os.remove("reNameCopyMove.json")

文本编码格式

  • Python2.x 默认是ASCII编码格式
  • Python3.x 默认是UTF-8编码格式 (UTF-8是Unicode的一种)

Python2.x 中第一行增加一行代码注释就可以处理Python文件中的中文导致的错误

# *-* coding:utf8 *-*   官方推荐
# coding = utf8

eval函数

eval()函数将字符串当成有效的表达式,并返回计算结果

print(eval("1+1")) # 输出2

eval函数不要滥用,不要直接转换input结果

网络编程

IP地址

目的:用来标记网络上的一台设备(唯一)

dest_ip 目标IP

src_ip 源IP

ip地址的分类

IP地址由四组数字组成 (0-255)

Python基础学习笔记_第7张图片
IP地址分类.png

端口

dest_port 目标端口

src_port 源端口

用来标识PC的应用进程

知名端口

知名端口0-1023 动态端口从1024-65535

80端口http服务 21端口分配给FTP服务

socket

import socket

# 创建一个socket   UDP
udp_socket = socket.socketpair(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口号
udp_socket.bind("端口号")
# 使用套接字收发数据  数据和对方的IP地址以及端口号
udp_socket.sendto("数据", ("192.158.33.23", 8080))
udp_socket.close()

UDP收发收据

同一个套接字可以收发数据

import socket
# UDP聊天器,并保存聊天记录
def main():
    # 创建一个socket
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    file = open("chatRecord.txt", mode="a", encoding="utf-8")

    while True:
        print("UDP聊天器")
        print("输入0结束程序")
        print("输入1发送数据")
        print("输入2接收数据")

        input_data = int(input())
        if 0 == input_data:
            file.close()
            break
        elif 1 == input_data:
            file.write(str(send_data(udp_socket)) + "\n")
        elif 2 == input_data:
            file.write(str(receive_data(udp_socket)) + "\n")
        else:
            print("输入错误")
    udp_socket.close()


# socket 接收数据
def receive_data(udp_socket):
    receive_data = udp_socket.recvfrom(1024)
    print("发送者:%s\n发送内容:%s" % (str(receive_data[1]), receive_data[0].decode("gbk")))

    return "发送者:%s\n发送内容:%s" % (str(receive_data[1]), receive_data[0].decode("gbk"))


# 使用套接字发送数据
def send_data(udp_socket):
    dest_ip = input("请输入对方的ip地址:")
    dest_port = int(input("请输入对方的端口号:"))
    send_data = input("请输入要聊天的内容:")
    udp_socket.sendto(send_data.encode("gbk"), (dest_ip, dest_port))
    return str(send_data.encode("utf-8"), encoding="utf-8")


if __name__ == '__main__':
    main()

TCP

TCP客户端收发数据

import socket


def main():
    # 创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.bind(("", 7788))
    # 连接服务器
    tcp_socket.connect(("192.168.28.90", 8080))

    send_data = input("请输入要发送的内容:")
    # 发送数据
    tcp_socket.send(send_data.encode("gbk"))

    # 接收数据
    receive_data = tcp_socket.recvfrom(1024)
    print("发送者:%s\n发送内容:%s" % (receive_data[1], receive_data[0].decode("gbk")))
    # 关闭套接字
    tcp_socket.close()


if __name__ == '__main__':
    main()

TCP服务端收发数据

import socket

# 创建socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定端口信息
tcp_server_socket.bind(("", 7000))

tcp_server_socket.listen(128)

new_client_socket, client_address = tcp_server_socket.accept()

print(client_address)

receive_data = new_client_socket.recv(1024)

print(receive_data.decode("gbk"))
new_client_socket.send("收到信息啦...".encode("gbk"))

new_client_socket.close()
tcp_server_socket.close()

多任务

多线程

线程执行的顺序不确定

调用线程的start()方法时才会创建线程,以及运行线程

    import time
    import threading
    
    
    def sing():
        for i in range(5):
            print("------唱歌--------")
            time.sleep(1)
    
    
    def dance():
        for i in range(5):
            print("=========跳舞========")
            time.sleep(1)
    
    
    def main():
        sing_thread = threading.Thread(target=sing)  # 注意这里是要用函数名,不能用函数名()->这个是调用函数
        dance_thread = threading.Thread(target=dance)
    
        sing_thread.start()
        dance_thread.start()
    
    
    if __name__ == '__main__':
        main()

多线程共享全局变量

全局变量的的修改:

在一个函数中对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局变量的指向进行了修改

如果修改了指向,即全局变量指向了一个新的地方,那么必须使用global

如果仅仅是修改了指向空间中的数据,此时不用使用global

多线程共享全局变量会导致资源竞争问题

互斥锁

lock = threading.Lock() #创建锁
lock.acquire()  # 上锁
lock.release()  # 释放锁

GIL

Python基础学习笔记_第8张图片
GIL全局解释器锁.png

你可能感兴趣的:(Python基础学习笔记)