python变量
- 没有类型声明
- 必须要赋初值
python中的可变(mutable)和不可变(immutable)对象
可变对象:list,dict,set,user-defined classes(unless specifically made immutable)
a = {} b = a print('id(a) = {}, id(b) = {}'.format(id(a),id(b))) a['yzc'] = 'nb' print('id(a) = {}, id(b) = {}'.format(id(a),id(b))) print(b['yzc'])
其输出为:
id(a) = 1789662989624, id(b) = 1789662989624
id(a) = 1789662989624, id(b) = 1789662989624
nb
从输出结果可知,a的值发生变化,a,b的地址相同且没有发生变化,并且a发生变化b也会相应的发生变化。不可变对象:int,string,float,tuple
i = 1 j = 1 print('id(i) = {},id(j) = {},id(1) = {}'.format(id(i), id(j), id(1))) i = i + 2 print('id(i) = {},id(j) = {},id(1) = {}'.format(id(i), id(j), id(1)))
其运行结果为:
id(i) = 1541696576,id(j) = 1541696576,id(1) = 1541696576
id(i) = 1541696640,id(j) = 1541696576,id(1) = 1541696576
可以看出由于i的值发生变化,导致地址发生了变化i = 1 j = i print('id(i) = {},id(j) = {},id(1) = {}'.format(id(i), id(j), id(1))) i = i + 2 print('id(i) = {},id(j) = {},id(1) = {}'.format(id(i), id(j), id(1)))
通过以上两个程序发现,当内存存储了一个数之后,就变得不可改变了,如果改变变量的值,相当于另开辟一块新的内存存储新的值。
Python中的字符串
- +操作符重载->字符串的拼接
- 字符串之前加‘r’可以表示原始字符串,而不是转义字符
- 多行字符串用三个引号包括,可以省去每行的换行符
python中的字符串格式化
- %-formatting
'%s %s' % ('one', 'two')
要注意,字符串和%之间没有逗号分隔,要写在一起
2. str.format()
3. f-strings
name = 'Tom'
age = 3
f'His name is {name},he is {age} yeas old'
python中的类型
- 整型
没有short,int,long之分,统一集成为整形 - 浮点型
可以用e计法来表示 15e8<=>15∗108 15 e 8 <=> 15 ∗ 10 8 - bool类型
bool类型支持数值运算,但是不建议使用
python中的类型转换
注:如果声明的对象名与python中内置的函数名一样,对象名会覆盖掉python内置的函数,导致其无法正常调用。
- type():查询变量类型,返回字符串
- isinstance():比较类型值是否一致,返回bool类型
python中的is vs ==
最近一直被 is 和==所困扰之前一直有一种错觉觉得is 就等价于==,但是在实际应用的时候总是会出现问题,用is程序莫名其妙的不对,也查不出错误,但是换成==,程序就会正常执行,因此特地查了查is vs ==,他们到底有什么区别
- is
is的功能是检查两个变量是不是引用了同一个对象 - ==
==的本质是检查两个对象是不是值相同
python解释器为1和True分别分配了不同的空间(通过打印id地址可以看出)1和True在取值上是相等的,但是地址不同。故当使用is的时候,打印结果为False,因为True和1并没有引用同一个对象。当使用==的时候,结果为True,因为==就是单纯的判断两个变量的值是否相等
python操作符
- +,+=
- -, -=
- *, *=
- **
**代表幂操作符,其优先级是比左侧操作符高,比右侧操作符低。 - /, /=
在python中除法会得自动得到一个浮点数,而不是像C语言那样确定类型。 - //
- 两个除号的除法的结果是整数(向下取整)
- and–对应&&
- or–对应||
- not–对应!
注: - python中比较运算符支持连续比较操作,eg:a<b<c其等价于(a<b)and(b<c)
操作符优先级 not>and>or
python条件操作符
三元操作符(x if condition else y)
f x < y:
small = x
else:
small = y
等价于
small = x if x < y else y
python 循环
- while
while 条件:
循环体
- for
for 目标 in 表达式/列表:
循环体
python中的list
python中直接用=赋值,是将引用赋值给新的变量,相当于两个变量指向同一片内存空间。如果想重新赋值到一片新的存储空间当中需要在列表后边加上:。因为slice会生成一个新的列表。
配合如下程序:
list1 = [1, 2, 3, 4];
list2 = list1
list3 = list1[:]
list1[3] = 6
print("List2 = ",list2)
print("List3 = ",list3)
其运行结果为:
由于list2是list1的引用,所以list1的值改变了,list2的值也会相应改变。list3是通过slice重新生成的序列,并不于list1共用一片内存,所以其值不改变
注:在list中[a:b]的取值范围是左开右闭,即list中[a,b)范围内所对应的值
python元组(tuple)
- List的内置函数
- 列表将数据放到中括号里,元组将数据放到小括号里,没有括号的一串数值默认缺省为元组
- 如果只有一个数,编译器不会将其视为元组。如果想要一个元素的元组,需要在数值后边加逗号
这里给出一个程序示例:
print(8*(8)
print(8*(8,))
其结果为:
如果在后边不加逗号,编译器理解为8*8的运算操作。加上逗号,编译器理解为8次的拷贝操作
- 元组的赋值方法
在元组中无法像列表那样直接赋值,因此元组需要利用slice重新开辟一片内存空间,将新的值写入到另一片内存空间中。并将元组的名字指向该内存。python解释器会自动将老的元组片段自动回收,防止内存泄漏。
示例程序如下:
tuple1 = 1,2,3,4,5,6
print(tuple1)
tuple1 = tuple1[0:3]+(7,)+tuple1[4:6]
print(tuple1)
其结果为:
python字符串
python中字符串中的方法参考小甲鱼的字符串方法及注释
python格式化
- format函数
- 位置 eg:{0},{1},{2}
关键字 eg:{a},{b},{c}
format中,可以位置参数和关键字混用,但是位置参数要在关键字之前python字符串中格式化符号的含义及转移字符含义
python函数
- def关键字定义函数
- return返回
- 函数的文档(doc)
- python函数支持关键字参数,防止参数过多,形参匹配发生错误
- python函数支持默认参数
- python支持变参
需要在形参前加入*,形似C语言指针。这样python是将输入参数打包到一个元组里,作为可变参数使用。
如果有可变参数,那么其可变参数后边的形参需要用关键字赋值,否则会出现二义性(报错)。为了避免错误,在定义函数的时候,通常给第默认参数后边的参数设置缺省值(默认参数)。
例如python中的print函数
print(*object,sep = '', end = '\n', file = sys.stdout,f lish = False)
在*object之后,每一个参数都有默认参数。
另外:
如果想将一个列表里边的每个变量当作一个可变参数,需要在列表名前加*
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
nums = [1, 2, 3]
calc(*nums)
- 关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
def person(name, age, **kw):
print('name', name,'age:', age, 'other',kw)
person('Bob', 35, city = 'Beijing', gender = 'M', job = 'Engineer')
当然也可以在外部生成一个字典传递给函数,但是要在实参前加**
def person(name, age, **kw):
print('name', name,'age:', age, 'other',kw)
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)
- 命名关键字参数
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符,后面的参数被视为命名关键字参数。
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错(TypeError):
- 在python中只有函数,没有过程,任何对象都是有返回值的,如果没有返回值,返回的是None
- 函数可以返回多个值,通过list或者tuple打包返回。
- 全局变量在函数中可以访问
- 在函数中不能修改全局变量的值,因为一旦修改全局变量的值,python解释器会在栈上重新创建一个新的同名变量,对全局变量的操作会施加到新的变量上,并不会改变全局变量的值,并且导致全局变量无法访问。
- golbal 关键字修饰,可以避免函数覆盖全局变量
闭包:
定义:
- 在函数内部嵌套函数
- 在嵌套的函数内部使用外层函数的局部变量
作用:
提高代码可复用性
def Func(a, b): def Line(x): return a*x + b return Line line1 = Func(2, 4) line2 = Func(3, 5) print(line1(1),line2(2))
提高并行性(流水并行),闭包可以封装函数,让函数只有一个参数输入,让流水线跑得更加顺畅
关于无法使用外部局部变量进行运算的解决方案(可以赋值)
通过容器来赋值(list)
def Func(): z = [2] def Line(x): z[0] += 1 return z return Line print (Func()(4))
通过nonlocal关键字修饰,告知编译器
def Func(): z = 2 def Line(x): nonlocal z z += 1 return z return Line print (Func()(4))
闭包的注意事项:
返回的函数并没有立刻执行,而是直到调用了闭包才执行:def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()
其f1, f2, f3的值都是9,因为只有三个函数都迭代完成才会返回i的值,此时i的值为3.
- lambda函数
定义方式:
g = lambda x: x+1; #lambda 变量: 返回值 print(g(1))
- Python 中一些关键函数
- filter
- map
python 递归:
Fibonacci数列:
#Fibonacci,F(1) = 1; F(2) = 1; F(N) = F(N-1) + F(N-2) def Fibonacci(length): if length<1: print("Exception: out of range of Fibonacci") elif (length == 1) or (length == 2) : return 1 else: return Fibonacci(length-1) + Fibonacci(length-2) for i in range(10): print(Fibonacci(i))
汉诺塔:
def hanoi(lay_num, a, b, c):#a柱子上的盘子借助b柱子到达C柱子 if(lay_num < 1): print('Exception: out of the range') elif(lay_num == 1): print(a, '->', c) else: hanoi(lay_num-1, a, c, b )#a柱子上的盘子借助c柱子到达b柱子 print(a, '->', c) hanoi(lay_num-1, b, a, c)#b柱子上的盘子借助a柱子到达c柱子 hanoi(3, 'a', 'b', 'c')
python 字典
- python字典初始化三种方式(value必须是字符串)
dict1 = {'1':'one', '2': 'two'}
dict2 = dict(((1,'one'),(2,'two')))
前两种key可以是数字或者字符串,不能是未定义的变量名。dict3 = dict(a = 'one',b = 'two' )
第三种方式,不能用数字或者字符串作为左值,即dict3 = dict(1 = 'one',2 = 'two' )
和dict3 = dict('a' = 'one','b' = 'two' )
都是错误的
- 字典中的内置函数
python的删除操作
在python中等于空,并不等价于对象被删除,需要调用clear函数,才能彻底删除a = {'yzc': 'father'} print(a) b=a print('id(a) = ', id(a), 'id(a) = ', id(b)) a = {} print(a, b) print('id(a) = ', id(a), 'id(a) = ', id(b))
其结果为:
从结果中可以看出,当a赋值为空的时候,a的id发生变化,也就意味着,编译器重新让a指向了一个新的空字典对象,而之前的字典对象并没有被释放掉。
python中的,赋值,shallow copy 和 deep copy
- 赋值( 直接用= )
对于类的直接赋值的话相当于赋值引用,其对象的id是相同的 - shallow copy
在python中,shallow copy处理方式是:如果拷贝的是一个复杂的类型(有嵌套关系),被嵌套的部分将会被共用,没有嵌套的部分会在内存中重新拷贝一份。
- copy 函数
copy函数在list,dict,tuple类被创建的时候会自动创建,也可以improt copy模块 - slice 赋值:
copied_list = original_list[:]
- copy 函数
- deep copy(deepcopy 函数)
变量内所有的内容都会被重新拷贝一份放到内存里。
python 中的集合(set)
- 用大括号包含的元素,没有映射关系的话,就会视为集合
- 集合中所有元素的值都是唯一的(不可重复)
- 不支持索引
- 没有顺序
- frozenset无法对集合内的元素做更改
文件操作
- open函数打开文件
其中路径要用两个斜杠(//)进行转义 文件的打开模式和文件对象方法
文件对象用完,一定要用close函数关闭,因为文件被读取到内存进行操作,掉电丢失,只有在关闭文件的时候才会被写回硬盘中。如果不及时close文件,很有可能文件在内存中,并没有保存到磁盘里,进而导致文件内容的丢失。支持用for循环直接读取文档内容,无需转换成列表
OS模块(系统模块)/os.path模块
要想rmdir(删除路径,先用remove删除路径下的文件)
pickle模块
pickle模块的主要目的是将文本文件和一种pickle的二进制文件之间的转换。
pickle的优势:- 占用更少的空间
- pickle是一种python内嵌的二进制文件编码方式,因此通过使用pickle可以解决可移植性的问题。
python中的exception
Python中异常的种类
异常检测:
try-except-finally语句
try: 异常检测 except Exception[as reason]: 出现异常(Exception)后的处理代码 finally: 无论如何都会执行的代码
伪代码中的Exception可以是一个tuple(Exception1, Exception2….)。
try: sum = 1+'1' except (OSError, TypeError) as reason: print('Exception'+str(reason))
运行结果为:
Exception unsupported operand type(s) for +: ‘int’ and ‘str’
try: f = open('abc123.txt','w') print('document has existed') except OSError as reason: print('Exception'+str(reason)) finally: f.close()
另外一个值得注意的是,当try语句中有异常与except语句匹配,finally部分的代码仍会在except语句执行之后执行。其好处就是能够防止异常发生后,某些操作没来的及做,或者被打断。(例如打开一个文件,发生异常后,文件没有及时关闭,可能造成文件内容丢失)
with语句对上述问题的优化,在with open语句代码块内对文件操作,出代码块,文件会被自动关闭
with open() as object
raise语句可以引入一个异常
python中的else语句:
- if…else…
while…else…
def ShowMaxFactor(num): count = num // 2 while count > 1: if num % count == 0: print("%d is the maximum factor of %d" %(num, count)) count -= 1 else: print('%d is a prime' %num) num = int(input (" input a number please: ")) ShowMaxFactor(num)
该程序目的是寻找最大约数,while语句循环执行结束会执行else语句,如果while语句被break中断,则else语句部分不会被执行。
try…else…
当except类型没有明确被指出时,为了代码健壮性,使用else语句,应对位置异常。类似于C++中的catch(…)
python中的对象
对象的概念
- 对象 = 属性 + 方法
- 对象的特点:封装,继承 和 多态
面对对象编程
- self:等价于C++中this指针,类的内部调用self来调用自身
__init__(self, param1, param2 ….): 构造方法
param1,2这些参数最好设置默认值,防止出现构造的时候参数不匹配而报错。
该魔法方法返回值必须是None私有和共有:
python通过name mangling技术来划分private和public属性,即通过特殊的命名方式赋予私有属性。
python默认属性和方法都是公有的
在方法或属性命名中,最前方加双下划线,将其变为私有属性继承class DerivedClassName(BaseClassName):
子类会覆盖父类同名的方法和属性。- 子类初始化父类方法:
- 调用未绑定的父类方法
在子类的构造方法中BaseClassName.__init__(self)
- 使用super函数
super().__init__()
super函数可以避免菱形多继承导致的多次初始化问题
- 调用未绑定的父类方法
- 子类初始化父类方法:
类,类对象,实例对象
class C: x = 1 a = C() b = C() c = C() c.x = 10 C.x = 100 print(f'a.x = {a.x},\nb.x = {b.x},\nc.x = {c.x}')
其结果为:
a.x = 100,
b.x = 100,
c.x = 10
在类定义的同时,会生成一个类对象,当类对象被实例化后会生成一个实例化对象,改变实例化对象的属性,会生成一个新的x属性,并且覆盖掉类对象的属性。类的属性和对象重名的时候,属性会覆盖方法,导致方法无法调用,因此:
- 不要试图在一个类里边定义出所有能想到的情况和方法,应该利用继承和组合机制来进行扩展
- 用不同词性命名:属性->名词,方法->动词
绑定:python严格要求方法需要有实例才能被调用
因为类对象没有self,只有实例化之后的对象才有self,因此在类的方法中调用需要使用self的方法,会报错,同时如果类中的函数第一个变量没有写self,实例化对象调用该方法时会出错。- __new__():实例化的时候被调用,唯一一个在__init__之前被调用的方法。
作用:customize the instance creation,大部分不需要重写,主要是需要修改不可变对象的时候需要重写 - __del__(self):析构方法,由于采用copy-on-write技术,所以只有当所有引用都被删除的时候,才会调用析构方法
用魔法方法要注意防止出现无限递归的现象
class A(int): def __add__(self, other): return self + other a = A(1) b = A(2) a+b
因为
return self + other
相当于再次调用加法函数将self和other相加。
正确方法是:return int(self) + int(other)
python会根据从左向右的顺序匹配操作符运算,例如:a + b,编译器会先查询a是否支持加法运算,然后再对b进行查询。
- 反运算:注意反运算传入的self值是右侧的操作数,因此在运算时需要注意顺序问题
class A(int):
def __rsub__(self,other):
return self - other
a = A(3)
5-a
其结果为-2,因为用self-other,self传入的是a的值,所以运算方向反了。
属性访问
__getattr__(self, name)
定义当用户试图获取一个不存在的属性时的行为__getattribute__(self, name)
定义当该类的属性被访问的定位__setattr__(self, name, value)
定义当一个属性被设置时的行为__delattr__(self, name)
定义当一个属性被删除时的行为
属性访问容易进入死循环的陷阱
class Rectangle: def __init__(self, width = 0, height = 0): self.width = width self.height = height def __setattr__(self, name, value): if name == 'square': self.width = width self.height = height else: self.name = value def getArea(self): return self.width * self.height Rectangle(4, 5)
由于每次
self.name = value
的时候都要调用__setattr__()
,所以构成了一个没有出口的递归。
解决方法:- 使用super函数调用基类的
__setattr__()
。 - 调用
ClassName.__dict__[name]
的方式来赋值.
描述符:将某种特殊类型的类的实例指派给另一个类的属性
__get__(self, instance ,ower)
用于访问属性,它返回属性的值__set__(self, instance, value)
将在属性分配操作调用,不返回任何内容__delete__(self, instance)
控制删除操作,不返回任何内容
迭代和迭代器
注意:python迭代器中的变量不能在迭代结束之前不能随意改动,改动迭代器需要的变量会影响迭代器的运行结果(因为迭代器是用一步算一步的)