一、函数定义
例子:
def printSentence(sentence): # 函数定义
"""
输出简单语句
:return:
"""
print(sentence) # 输出
return sentence # 返回的讯息
print(printSentence('hello world')) # 函数调用,当鼠标放在函数上面时会显示函数定义时提示讯息
参数类型
①必选参数
def sum(a, b): # a,b都为必选参数
sum = a + b
print(sum)
pass
sum(10, 20)
②默认参数(缺省参数)
要注意的时在默认参数后面不能有必选参数(如:(a,b=20,c)是不可以的)
def sumNum(a, b=10): # a为必选参数,b为默认参数
sum = a + b
print(sum)
pass
sumNum(10, 20) # 30
sumNum(10) # 20
③可变参数
当参数的个数不确定时使用,*来定义,且要在关键字参数之前
def getCompute(*args): # 可选参数为 * 加变量名
"""
获得可变长的数据序列
:param args: 不定长的数据序列
:return: void
"""
print(args) # args为元组类型
pass
getCompute(10, 20, 30)
getCompute(10)
④关键字参数
在函数题内,关键字参数是一个字典类型,** 来定义,且参数的 个数不限
def getCompute(**keyArgs):
print(keyArgs)
pass
dictA = {'age': 10, 'name': '李华'}
getCompute(**dictA) # {'age': 10, 'name': '李华'}
getCompute(age = 10, name = '李华') # {'age': 10, 'name': '李华'}
综合例子
def getCompute(*args, **keyArgs): # 可任意输入参数
print(args)
print(keyArgs)
pass
getCompute(20, [1, 2], (1,), name = '李华') # 输入的参数会自行匹配变量
# (20, [1, 2], (1,))
# {'name': '李华'}
返回值
return可以返回一个值或多个值,当返回一个值时返回的是那个值;而返回多个值时则返回一个包含多个值的元组;当有返回多个值且有多个变量接收时值会被一一对应赋值
局部变量与全局变量
- 当局部变量与全局变量同时出现时,局部变量优先级更高;且这两个变量为完全不同的两个变量(变量的内存地址不同)
- 要注意当 想改变全局变量且这个变量为不可变类型(str、int、tuple、float) 时需要使用global来提升变量为全局变量,而当想改变全局变量且这个变量为可变类型(dict、list) 时不需要使用global来提升变量就可以更改变量里面的值
- 而nonlocal与global有异曲同工之妙,nonlocal是对非全局变量的提升,当然nonlocal也只是针对不可变类型的变量
- 函数locals()用来查看本地变量有哪些,并以字典的形式输出
- 函数globals()用来查看全局变量有哪些,并以字典的形式输出
strA = 'nihao' # 全局变量
def getMemoryAddress():
strA = 'world' # 局部变量
print(strA) # world
print(id(strA)) # 2732791428528
pass
getMemoryAddress()
print(strA, id(strA)) # nihao 2732791524656
可以通过global关键字来把局部变量提升为全局变量
strA = 'nihao' # 全局变量
def getMemoryAddress():
global strA # 变量提升
strA = 'world'
print(strA) # world
print(id(strA)) # 2177461122480
pass
getMemoryAddress()
print(strA, id(strA)) # world 2177461122480
可以通过nonlocal关键字来提升局部变量
def getMemoryAddress():
strA = 'nihao' # 局部变量
def internal_fun():
nonlocal strA # 变量提升
strA = 'world'
print(strA) # world
print(id(strA)) # 2478225832496
pass
internal_fun()
print(strA, id(strA)) # world 2478225832496
pass
getMemoryAddress()
所以我们了解到在python中万物皆对象,且在函数调用的时候实参传递的时对象的引用(地址)
匿名函数
python中使用lambda关键字创建匿名函数,所谓匿名即这个函数没有名字不用使用def关键字创建标准的函数;它自带return,且return的结果为表达式的结果,它一般用做参数传入函数
语法:lambda 参数1,参数2,参数3:代码表达式语句
例如:
far = lambda x, y: x if x > y else y # 匿名函数,这里是简单的三元运算符
def numCompare(x, y): # def定义函数
if x > y:
return x
return y
print(numCompare(10, 20)) # 30
print(far(10, 20)) # 30
map函数
map函数会根据提供的函数对指定序列做映射。第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
map(function, iterable, …)
list_all = (1, 2, 3, 4, 5)
result = map(lambda x: x + 4 if x % 2 == 1 else x, list_all)
print(list(result))
# 输出结果
[5, 2, 7, 4, 9]
reduce函数
函数会对参数序列中元素进行累积。函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
reduce(function, iterable[, initializer])
from functools import reduce
list_all = (1, 2, 3, 4, 5)
result = reduce(lambda x, y: x + y, list_all)
print(result)
# 输出结果
15
filter函数
函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
filter(function, iterable)
list_all = (1, 2, 3, 4, 5)
result = filter(lambda x: x > 2, list_all)
print(list(result))
# 输出结果
[3, 4, 5]
递归函数
递归函数定义时要注意两个方面,一个是有明确的结束条件,另一个是自己调用自己;它有逻辑简单等优点,但同时它会更加容易导致栈溢出等问题;可用于实现树形结构的遍历
import os # 导入文件处理模块
def searchDir(path):
file_paths = os.listdir(path) # 获得当前地址下的所有文件与文件夹
for file_name in file_paths: # 遍历该列表
full_path = os.path.join(path, file_name) # 地址拼接
if os.path.isdir(full_path): # 判断该路径下是否为文件夹
searchDir(full_path) # 开始递归
pass
else:
print(full_path) # 否则打印
pass
pass
else: # 退出条件,不过在该情况下可以省略
return
searchDir('F:\cEnv') # 函数调用
二、内置函数
python的内置函数传送门
数学运算
abs() – 求绝对值
round(x[, n]) – 对x取近似值,保留n位小数,默认为四舍五入,若n大于实际小数部分长度则不进行操作
pow(x, y[, z]) – 函数是计算 x 的 y 次方,如果 z 在存在,则再对结果进行取模,其结果等效于 pow(x,y) %
divmod(a, b) – 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)
max(iterable) – 最大值,其参数可为x, y, z, …
min(iterable) – 最小值,其参数可为x, y, z, …
sum(iterable[, start]) – 对序列进行求和计算。iterable为可迭代对象(如:列表、元组、集合),start为指定相加的参数,如果没有设置这个值,默认为0
eval(expression[, globals[, locals]]) – 用来执行一个字符串表达式,并返回表达式的值。expression – 表达式。globals – 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。locals – 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
类型转换函数
int() – 转为int,下取整
float() – 转为float
str() – 转为str
ord() – 字符转数字
chr() – ASCII码转为字符
bool() – 转为boolean类型
bin() – 转为二进制
hex() – 转为十六进制
oct() – 转为八进制
list() – 元组转为list
tuple() – 列表转为tuple
dict() – 创建dict
bytes() – 转为字节数组
序列操作
all() – 用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。
any() – 用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。
sorted() – 对所有可迭代的对象进行排序操作。sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
reverse() – 用于反向列表中元素。
range() – 可创建一个整数列表
zip() – 用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
enumerate() – 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
三、面向对象
面向过程是指按照解决问题的步骤进行编写程序,而面向对象是指把事物的具体特征抽象出来形成类再进行编写程序的
类和对象
类是一个模板,模板里面可以有方法;而对象是根据类创建的实例,可通过实例对象使用类里面的方法;并且在类的内部,使用def关键字可以定义一个实例方法,与一般函数定义不同,实例方法必须包含参数self且它必须为第一个参数(名字不一定为self,这里是约定俗成);同时在类内可以定义不同的类属性(方法外面定义的属性)与实例属性(在方法内使用self.属性名定义而得的);无论是实例方法还是实例属性都是属于实例本身的;python构造方法包括创建对象和初始化对象,在python当中,分为两步执行:先执行__new__方法,然后执行__init__方法,同时头尾双下划线的方法(属性)也被叫做魔法方法(属性),魔术方法会在某个特殊的时候被自动执行
class Person: # 声明一个类
name = '李明' # 类属性
age = 20
def __init__(self, gender, height): # 魔法函数(初始化函数)
self.gender = gender # 实例函数
self.height = height
def eat(self, weight): # 实例方法
self.weight = weight # 实例方法
print('姓名:{} 班级:{} 性别:{} 身高:{} 体重:{}'.format(self.name, self.className, self.gender, self.height, self.weight))
pass
ming = Person('male', 175) # 用类实例化一个对象,并传入默认值
ming.className = '二班' # 给ming对象添加一个实例属性
print(ming.name) # 输出类属性
ming.eat(140) # 调用实例方法
self
self和实例对象指向同一个内存地址,可以认为self就是实例对象,相当于java里面的this
class Person:
def eat(self):
print('地址为:{}'.format(id(self)))
pass
ming = Person()
ming.eat() # 地址为:2553277332304
print(id(ming)) # 2553277332304
魔术方法
__init__() – 是当实例对象创建完成后被触发的,然后设置对象属性的一些初始值。
__str__() – 是在print对象时被触发,打印对象的信息以测试的时候使用,因为如果不用的话会就只输出对象的地址而不会有其他的信息,注意要用return,与java里面的toString()类似
__new__() – 是在实例创建之前被触发的,因为它的任务就是创建实例然后返回该实例,先于init函数的执行
__call__() – 在把对象当作函数来调用时触发,且执行该函数里面的内容(例如:它可以执行 x1(1,2)这样的函数,而不是x1.__call__(1, 2))
__del__() – 在程序运行结束后进行对象销毁的时候触发这个方法,来释放资源
例如:
__init__()
# __str__()
class Person:
def __init__(self, name):
self.name = name
pass
pass
ming = Person('李华')
print(ming) # <__main__.Person object at 0x000001CAE51C3B50>
__str__()
class Person:
def __init__(self, name):
self.name = name
pass
def __str__(self):
return '输出对象的提示信息' # 注意这里要用return
pass
ming = Person('李华')
print(ming) # 输出对象的提示信息
__new__()
注意事项:
- __new__是在一个对象实例化的时候所调用的第一个方法
- __new__至少必须要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,其他的参数是用来自动直接传递给_init_方法的
- 在__new__方法中,不能调用自己的__new__方法,即:return cls.__new__(cls)
例一:
class Person:
def __init__(self, name): # 第二执行
self.name = name
print('init被执行')
pass
def __str__(self):
return 'str输出对象的提示信息'
pass
def __new__(cls, *args, **kwargs): # 第一执行,args与kwargs自动提交给init函数
print('new被执行')
print(cls) # cls为class的缩写,代表需要实例化哪一个类,由python解释器自动提供
print(args) # 和下面的kwargs同为参数
print(kwargs)
return object.__new__(cls) # 返回创建好的一个对象
ming = Person('李华')
print(ming)
例二:
class Animal:
def __init__(self, name):
self.name = name
pass
def __new__(cls, *args, **kwargs): # args与kwargs自动提交给init函数
print('对象被创建了')
# return super().__new__(cls) # 创建类方法一
return object.__new__(cls) # 创建类方法二
pass
ming = Animal('李明')
print(ming.name)
__call__方法
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, a, b): # 定义call魔术方法
self.a = a
self.b = b
print('打印call方法({}, {})'.format(self.a, self.b))
x1 = Foo(1, 2) # 创建对象
x1(1,2) #打印结果为:打印call方法(1, 2),将一个类实例对象当做函数调用,直接执行__call__方法,而不是x1.__call__(1, 2)
__del__()方法(析构方法)
当一个对象没有在被引用的时候,它会被删除或被销毁,此时python解释器会默认调用一个方法,这个方法为 __del__(),它也被称为析构方法,简单的说这个函数并不对对象进行销毁,而只是在系统内部执行销毁时调用这个函数;del关键字可以销毁对象
class Animal:
def __init__(self, name):
self.name = name
pass
def __del__(self):
print('对象被删除了')
pass
def __new__(cls, *args, **kwargs):
print('对象被创建了')
return object.__new__(cls)
pass
ming = Animal('李明')
print(ming.name)
类属性与实例属性
类属性就是类拥有的属性,它被该类的所有实例对象共有,类和实例都可以访问它;实例属性为对象所有的属性,只能通过实例对象访问;要注意的是不同的实例对象的类属性是同一个指向(地址相同);同时出现类属性与实例属性时,实例属性优先级高
class Father(object):
weight = 100 # 类属性
def __init__(self, name):
self.name = name # 实例属性
pass
pass
ming = Father('李明')
print(ming.name) # 李明,可以访问
print(ming.weight) # 100,可以访问
print(Father.weight) # 100,可以访问,注意这里不能用super()
# print(Father.name) # 不可以访问,类不可访问实例属性
ming.weight = 200 # 不可这样修改类属性,因为他这样实际是创建了一个实例属性
print(ming.weight) # 200
print(Father.weight) # 100
Father.weight = 200 # 这样可以修改类属性
print(ming.weight) # 200
print(Father.weight) # 200
类方法与静态方法
-
类方法为类所拥有的方法,需要用装饰器@classmethod来标识其为类方法,对于类方法它的第一个参数必须为这个类,一般以cls命名,类方法可以通过类与实例对象来调用,类方法的执行不依赖于实例对象
-
静态方法为类对象所拥有,需要用@staticmethod来标识静态方法,没有默认参数,静态方法可以通过类与实例对象来调用,不过我们一般不用实例对象来访问静态方法,所以静态方法里面的代码都是与业务逻辑无关的,其功能是独立的
-
类属性、实例方法、类方法、静态方法在内存中只保存⼀份,且保存在类中。而实例属性在每个实例对象中都要保存⼀份。
-
因为 __class__ 的存在使实例对象可以访问类属性、实例方法、类方法、静态方法、实例属性。
-
类只能访问类属性、类方法、静态方法。类不能访问实例属性的原因是实例属性存储在实例中。虽然实例方法是定义在类中但是只有那些被装饰器装饰过的方法才可以被类调用
import time # 引入包
class Father(object):
@classmethod # 类方法
def dance(cls):
print('跳舞')
pass
@staticmethod # 静态方法
def show_time():
time.sleep(1) # 休眠一秒
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
pass
pass
ming = Father()
ming.dance() # 跳舞
Father.dance() # 跳舞
ming.show_time() # 2020-11-08 17:27:13
Father.show_time() # 2020-11-08 17:27:13
私有属性与私有方法
- 为了保护属性(方法)安全使之不能在类的外部直接被访问,则将属性(方法)定义为私有属性(方法)。
- 定义方法:两个下划线开头。
- 要注意的是私有属性(方法)不会被它的派生类继承
- 要注意的是这里的私有不是真正的私有,而是名义上的私有。因为他的底层只是把私有属性(方法)改了个名字,通过这个名字就可以调用私有属性(方法)方法但是不建议这样调用。可使用dir()函数查看
- dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表
class Animal: # 父类
__names = '李明' # 私有属性
def __init__(self, age):
self.__age = age # 私有属性
pass
def __sing(self): # 私有方法
print('Animal,唱')
pass
pass
class Duck(Animal): # 子类
def __init__(self, age):
super(Duck, self).__init__(age)
pass
pass
tiger = Animal(10)
print(dir(Animal)) # 使用dir()函数打印当前范围内的变量、方法
print(dir(tiger))
print(Animal._Animal__names) # 调用类属性
Animal._Animal__sing(tiger) # 调用实例方法
# print(tiger.__names) # 无法调用
# print(tiger.__age) # 无法调用
# tiger.__sing() # 无法调用
duck = Duck(20)
# print(duck.__names) # 无法调用
# print(duck.__age) # 无法调用
# duck.__sing() # 无法调用
# 输出结果
['_Animal__names', '_Animal__sing', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
['_Animal__age', '_Animal__names', '_Animal__sing', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
李明
Animal,唱
property
因为在访问私有属性时必须通过类里面的方法调用,这样很别扭,因为我们想用属性却调用了方法,所以property应运而生,他解决了在使用属性时调用方法的这种奇怪场景。它有两种实现方法:①利用property属性函数②利用装饰器,但要注意顺序
class Animal:
__names = '李明'
def __init__(self, age):
self.__age = age # 私有属性
pass
def get_age(self): # 获得私有属性的值
return self.__age
def set_age(self, age): # 设置私有属性的值
self.__age = age
pass
age = property(get_age, set_age) # 属性函数
@property # 提供一个getter方法,注意property与names.setter的顺序
def names(self):
return Animal.__names
@names.setter # 提供一个setter方法
def names(self, names):
Animal.__names = names
pass
pass
tiger = Animal(10)
print(tiger.get_age()) # 10
print(tiger.names) # 李明
tiger.set_age(20)
tiger.names = '李华'
print(tiger.get_age()) # 20
print(tiger.names) # 李华
print('---------------------')
print(tiger.age) # 20
tiger.age = 30
print(tiger.age) # 30
单继承
子类可以重写父类的方法,且子类会继承父类的公开的方法与属性,子类的方法优先级大于父类的方法
class Animal(object): # 所有的原始类都是继承于object类
def __init__(self, name):
self.name = name
pass
def eat(self):
print('Animal:正在吃')
pass
def laugh(self):
print('Animal:正在笑')
pass
pass
class Dog(Animal): # 继承Animal类,这里的Dog为字类,Animal为父类
def bark(self):
print('Dog:狗叫')
pass
def eat(self): # 重写父类的eat方法
print('Dog:正在吃')
pass
pass
ming = Dog('巴哥')
ming.bark()
ming.eat() # 先在子类中查找方法,然后再在父类中查找
ming.laugh() # 子类调用父类的方法
print(ming.name) # 子类调用父类的属性
多继承
当多个父类中有相同的方法时,查询的顺序为广度优先,同级则从左到右
例如:
class A(object):
def move(self):
print('A.move的方法')
pass
pass
class B(A): # 继承A类
def move(self): # 重写方法
print('B.move的方法')
pass
pass
class C(A): # 继承A类
pass
class D(C, B): # 继承C, B类
pass
ming = D()
ming.move() # B.move的方法,查找的方式为D->C->B->A
print(D.__mro__) # __mro__方法用于解析程序的执行顺序
# 结果为:(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# 上面结果表示了类的继承顺序
继承的传递
class Grandfather(object):
def eat(self):
print('grandfather的eat方法')
pass
pass
class Father(Grandfather):
pass
class Son(Father):
pass
ming = Son()
ming.eat() # grandfather的eat方法
print(Son.__mro__) # 打印Son类的执行顺序
# (<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Grandfather'>, <class 'object'>)
子类调用父类方法
- 要注意这里的super()与super(type, obj)都是获得当前的类的父类,而super(type, obj)只是在底层进行了一个isinstance(type, obj)
- 这里的type是类名,obj为实例对象。类名可以看成自定义类型
class Father(object):
def __init__(self, name):
self.name = name
pass
def eat(self):
print('Father的eat方法')
pass
pass
class Son(Father):
def __init__(self, name, height):
# Father.__init__(self, name) # 调用父类的方法,手动检索父类,要添加self参数
super().__init__(name) # 调用父类的方法,自动检索父类,不过只能在类里面使用
self.height = height
pass
def drink(self):
Father.eat(self) # 调用父类的方法,手动检索父类,要添加self参数
pass
pass
ming = Son('李明', 175)
ming.eat() # Father的eat方法
ming.drink() # Father的eat方法
print(Son.__mro__) # (<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>)
print(ming.name) # 李明
print(ming.height) # 175
多态
多态是指同一个行为具有多个不同表现形式或形态,因为Python是天生支持多态的,且它的写法不同于Java和C#这一类强类型语言中的多态的写法,所以它可利用“鸭子类型”来实现多态;但要实现多态有两个前提:继承与重写。同时也要注意isinstance()函数的使用
class Animal: # 父类
def say_hello(self):
print('Animal, 你好')
pass
pass
class Duck(Animal): # 继承
def say_hello(self): # 重写
print('Duck, 你好')
pass
pass
class Dog(Animal): # 继承
def say_hello(self): # 重写
print('Dog, 你好')
pass
pass
def duckLike(param): # 多态实现的统一调用方法
param.say_hello()
pass
duckLike(Duck()) # 多态的实现,对同一行为有不同的表现
duckLike(Dog())
好处:它增加程序的灵活性与扩展性
单例模式
单例模式是常用设计模式的一种,也就是说这个整个系统中只有一个实例,重复打并也是使用这个实例。简单的说就是不管创建多少次对象,类返回的对象都是最初创建的,不会再新建其他对象。
class Animal:
__instance = None
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
if not cls.__instance: # 判断cls类里面是否有_instance属性
cls.__instance = object.__new__(cls)
return cls.__instance
pass
ming = Animal('李明')
hua = Animal('李明')
print(ming.name) # 李明
print(id(ming) == id(hua)) # True
动态添加属性和方法
import types # type库里的方法可以把方法添加实例中
class Student:
def __init__(self, name):
self.name = name
pass
pass
ming = Student('李米娜')
ming.age = 20 # 动态添加实例属性
print(ming.age) # 20
Student.weight = 100 # 动态添加类属性
print(Student.weight) # 100
def get_info(self): # 定义实例方法
print('{}有{}重'.format(self.name, self.weight))
pass
ming.print_info = types.MethodType(get_info, ming) # 动态添加实例方法
ming.print_info() # 调用定义好的实例方法
@classmethod
def eat(cls): # 定义类方法
print('你有{}斤了'.format(cls.weight))
pass
Student.print_eat = eat # 动态添加实例方法
Student.print_eat() # 类调用定义好的实例方法
ming.print_eat() # 实例对象调用定义好的实例方法
@staticmethod
def drink(): # 定义静态方法
print('喝水')
pass
Student.print_drink = drink # 动态添加实例方法
Student.print_drink() # 类调用定义好的实例方法
ming.print_drink() # 实例对象调用定义好的实例方法
__slots__变量
python是动态语言,在运行的时候可以动态添加属性。如果要限制在运行的时候给类添加属性,Python允许在定义class的时候,定义一个特殊的 __slots__变量,来限制该class实例能添加的属性。只有在 __slots__变量中的属性才能被添加,没有在_slots_变量中的属性会添加失败。它可以防止其他人胡乱添加属性和减少内存消耗。子类不会继承父类的__slots__属性,但当子类也声明__slots__属性时此时的slots值就为父类于子类的并集,所以注意slots不要重复写
class Animal:
__slots__ = ('name', 'age') # 父类限制添加的属性,这里也可以是列表类型
def __str__(self):
return '{}......{}'.format(self.name, self.age)
pass
class Duck(Animal):
__slots__ = ['gender'] # 子类声明属性限制
pass
tiger = Animal()
tiger.name = 'tiger' # 可以添加
tiger.age = 3 # 可以添加
# tiger.weight = 100 # 无法添加
print(tiger) # tiger......3
duck = Duck()
duck.name = 'tang' # 可以添加,此时的限制为子类与父类的并集
duck.gender = 'male' # 可以添加
# duck.weight = 100 # 不可以添加
print(duck.name) # tang
print(duck.gender) # male
四、异常处理
异常处理简单说明
- python里面的错误抛出机制是当程序在运行时发生异常,解释器会查找在该层的异常捕获类型是否可以捕获,如果不能捕获则将异常传递给上一层,直到最后一层,如果最后一层都没有捕获到则程序直接down,所以python只要在合适的层次去捕获就行了,这样可以减少我们书写错误捕获模块的代码
- 且一个错误处理块可以有多个except,Exception 包含了多个错误类型,但它无法对每个错误进行精确的描述以提醒用户
- 若使用Exception类型的错误,则必须写在其他的异常处理的后面
- finally里面的代码无论如何都会执行,也就是说如果try、except、else、finally里面都有return的时候,输出的永远是finally里面的return,因为其它块里面的return都被finally的return所覆盖了
try:
# print(a) # 命名错误
# listA = [1, 2, 3]
# print(listA[10]) # 索引错误
b = 10 / 0 # 除数为0错误
pass
except NameError as msg: # 命名错误捕获,一个块里面可以有多个except
print('except:当try里面的程序出错了则执行')
print(msg)
pass
except IndexError as msg: # 索引错误捕获
print('except:当try里面的程序出错了则执行')
print(msg)
pass
except ZeroDivisionError as msg: # 除数为0错误捕获
print('except:当try里面的程序出错了则执行')
print(msg)
pass
except Exception as msg: # Exception包含了多个错误类型
print('except:当try里面的程序出错了则执行')
print(msg)
pass
else:
print('except:当try里面的程序没有出错则执行')
pass
finally:
print('finally:无论try里面的程序出没有出错都执行')
pass
自定义异常
自定义异常都需要直接或间接继承Error或Exception,且要使用raise关键字
class TestMyException(Exception): # 定义的异常类
def __str__(self):
return '您输入的内容超长了'
pass
if len(input('请输入内容(最大为2):')) > 2:
raise TestMyException() # 使用raise关键字报错
五、闭包与装饰器
闭包
- 闭包就是在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包,它可以保存保存外函数的状态
- 一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束
def outer(a): # outer是内函数
b = 10
def inner(): # inner是内函数
s = a + b
print(s)
pass
return inner # 返回一个函数引用
pass
print(outer(11) == outer(12)) # False
print(outer(11) is outer(12)) # False
fun1 = outer(11) # <function outer.<locals>.inner at 0x000001FD8FCD24C0>
fun2 = outer(12) # <function outer.<locals>.inner at 0x000001FD8FEE74C0>
print(fun1, fun2, sep='\n')
还不懂的可以点击这里
装饰器
- 装饰器有两个特点:①函数是作为参数出现的,②要有闭包的特点
- 使用装饰器的目的:为了扩展函数但又不想修改原函数的代码,且想使用原函数名来调用
- 装饰器的执行顺序(以下面的代码为例):①下面的@decorator会执行decorator函数,而它下面的函数(被装饰函数)则会在系统底层被当成参数传给decorator函数;②decorator函数执行,并返回wrapper函数的指向;③返回的指向在系统底层被赋值给bar;④调用函数bar,则实质上是调用函数wrapper
- 要注意的是若有多个装饰器,则执行离被装饰函数最近的装饰器,以此类推
- 若装饰器带参数则写三层,不带参数则写两层
不带参数的装饰器
def decorator(fun): # 装饰器,需要扩展的内容,里面的参数被自动赋值为bar函数
# 这里为两层
num = 100
def wrapper(*arg, **kwargs): # 以防被装饰函数的参数个数不定
print(num)
fun(*arg, **kwargs) # 执行bar函数,对arg解包
print(num)
pass
return wrapper # 返回函数的引用
pass
@decorator
def bar(strA): # 被装饰函数,一个参数
print('bar函数里面语句', strA)
pass
bar()
print(bar) # 打印bar函数,这里实质为wrapper函数
@decorator
def bar1(strA, num): # 被装饰函数,两个参数
print('bar函数里面语句', strA, num)
pass
bar1('tom', 20)
# 函数执行结果
100
bar函数里面语句 hello
100
<function decorator.<locals>.wrapper at 0x000002ACDC2B74C0>
100
bar函数里面语句 tom 20
100
带参数的装饰器
def outer(n): # 外层函数,共有三层
print('数字为{}'.format(n))
def decorator(fun): # 需要扩展的内容,里面的参数被自动赋值为bar函数
num = 100
def wrapper(*arg, **kwargs): # 以防被装饰函数的参数个数不定
print(num)
fun(*arg, **kwargs) # 执行bar函数,对arg解包
print(num)
pass
return wrapper # 返回函数的引用
pass
return decorator
@outer(10)
def bar(strA): # 被装饰函数,一个参数
print('bar函数里面语句', strA)
pass
bar('hello')
print(bar) # 打印bar函数,这里实质为wrapper函数
# 输出结果
数字为10
100
bar函数里面语句 hello
100
<function outer.<locals>.decorator.<locals>.wrapper at 0x000001F517307C10>
六、推导式
列表推导式
列表推导式就是对旧列表进行某些操作然后返回一个新列表
语法:[out_express for out_express in input_list if out_express_condition]
例1:
list_name = ['tom', 'lily', 'abc', 'jack', 'steven', 'bob', 'ab', 'c']
print([name.capitalize() for name in list_name if len(name) >= 3]) # 列表推导式
new_list_name = [] # 下面的代码与上面的列表推导式等效
for name in list_name:
if len(name) >= 3:
new_list_name.append(name.capitalize())
print(new_list_name)
# 输出结果
['Tom', 'Lily', 'Abc', 'Jack', 'Steven', 'Bob']
['Tom', 'Lily', 'Abc', 'Jack', 'Steven', 'Bob']
例2:
# 输出元组列表,前一个值为偶数,后一个值为奇数。类似于循环嵌套
print([(x, y) for x in range(5) if x % 2 == 0 for y in range(10) if y % 2 == 1])
# 输出结果
[(0, 1), (0, 3), (0, 5), (0, 7), (0, 9), (2, 1), (2, 3), (2, 5), (2, 7), (2, 9), (4, 1), (4, 3), (4, 5), (4, 7), (4, 9)]
例3:
# 对小于5000的加500,大于5000的加200
list1 = [{'name': 'tom', 'salary': 5000},
{'name': 'lucy', 'salary': 8000},
{'name': 'jack', 'salary': 4500},
{'name': 'lily', 'salary': 3000}]
new_list = [employee['salary'] + 200 if employee['salary'] > 5000 else employee['salary'] + 500 for employee in list1]
print(new_list)
# 输出结果
[5500, 8200, 5000, 3500]
集合推导式
集合推导式类似于列表推导式,只是把[]换成{}并返回一个新的集合
# 对列表里面的值进行加1并去重
list1 = {7, 1, 2, 3, 4, 5, 1, 4, 9}
new_set = {value + 1 for value in list1} # 集合自带去重
print(new_set)
# 输出结果
{2, 3, 4, 5, 6, 8, 10}
字典推导式
字典推导式类似于列表推导式,只是把[]换成{}并返回一个新的字典
# 把键值对的键与值进行交换
dict1 = {'name': 'tom', 'salary': '5000', 'gender': 'male'}
new_dict = {value: key for key, value in dict1.items()}
print(new_dict)
# 输出结果
{'tom': 'name', '5000': 'salary', 'male': 'gender'}
七、生成器
- 在Python中,一种一边循环一边计算的机制,称为生成器: generator
- python中的generator保存的是算法,真正需要计算出值的时候才会去往下计算出值。它是一种惰性计算(lazy evaluation)
- 它避免了大量空间的浪费,使我们可以在循环的过程中不断推算出后续的元素
- 生成器可以避免不必要的计算,带来性能上的提升;而且会节约空间,可以实现无限循环(无穷大的)的数据结构
- 利用生成器可用来实现协程(进程 - > 线程 -> 协程)
定义生成器方式
方法一:通过列表推导式
只需要把一个列表生成式的 []改成(),就创建了一个generator
generator = (x * 3 for x in range(5))
print(generator)
# 输出结果
<generator object <genexpr> at 0x000001A8E6C40AC0>
方法二:在函数里使用yield关键字
函数里有了yield后,执行到yield就会停住,当需要再往下算时才会再往下算。所以生成器函数即使是有无限循环也没关系,它需要算到多少就会算多少,不需要就不往下算。可以简单地把yield n 理解为 return n + 暂停
def foo(length): # 斐波那契数列
a = 0
b = 1
n = 0
while n < length:
yield(a)
flag = a # 这里也可以写成a, b = b, a + b
a = b
b = flag + b
n += 1
return '没有更多数据了' # 当超过长度时返回return里面的值作为异常里面的信息
generate = foo(8) # 这里并不执行生成器,而是生成生成器
print(generate)
print(generate.__next__())
print(next(generate))
print(next(generate))
print(next(generate))
print(next(generate))
print(next(generate))
print(next(generate))
print(next(generate))
print(next(generate))
# 输出结果
Traceback (most recent call last):
File "F:\pycharm\pythonProject\index.py", line 27, in <module>
print(next(generate))
StopIteration: 没有更多数据了
<generator object foo at 0x0000027B01350AC0>
0
1
1
2
3
5
8
13
21
34
使用生成器
- 每调用一次则产生一个值,而不是像列表生成器一样一股脑的生成全部的值
- 但当超过长度时,使用程序会报异常
方法一:生成器内置函数__next__()
generator = (x * 3 for x in range(5))
print(generator.__next__())
print(generator.__next__())
print(generator.__next__())
print(generator.__next__())
print(generator.__next__())
# print(generator.__next__()) 报出异常
# 输出结果
0
3
6
9
12
方法二:Python内置函数next()
generator = (x * 3 for x in range(5))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
print(next(generator))
# print(next(generator)) 报出异常
# 输出结果
0
3
6
9
12
方法三:for循环遍历生成器
def foo(length):
a = 0
b = 1
n = 0
while n < length:
yield(a)
a, b = b, a + b
n += 1
return '没有更多数据了'
generate = foo(8)
for value in generate:
print(value)
# 输出结果
0
1
1
2
3
5
8
13
当然其实平常很少用到next()或__next__(),我们直接用for循环就可以遍历一个generator,其实for循环的内部实现就是不停调用生成器的下一个值。
八、可迭代对象和迭代器
可迭代对象
可迭代对象有列表、元组、字符串、生成器、集合、字典
判断是否为可迭代的
from collections.abc import Iterable
print(isinstance('jack', Iterable))
print(isinstance([1, 2], Iterable))
print(isinstance(110, Iterable))
print(isinstance((1,), Iterable))
print(isinstance({1, 2}, Iterable))
print(isinstance({'name': 'mike'}, Iterable))
print(isinstance((value for value in range(5)), Iterable))
# 输出结果
True
True
False
True
True
True
True
迭代器
- 迭代器不等于可迭代对象,例如生成器既是迭代器也是可迭代对象,而列表是可迭代对象而不是迭代器。但是可以通过iter()函数把可迭代对象转换为迭代器
- 迭代器从第一个元素开始访问,直到所有的元素被访问完结束
- 迭代器只能往前不会后退
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
- 迭代器包含了生成器,而生成器不包含迭代器。所以就好如迭代器是一个集合,而生成器是它的一个元素
generator1 = (x * 3 for x in range(5))
print(next(generator1)) # 0
print(next(generator1)) # 3
print(next(generator1)) # 6
print(next(generator1)) # 9
print(next(generator1)) # 12
list_num = [1, 2, 3, 4, 5]
generator2 = iter(list_num)
print(next(generator2)) # 1
print(next(generator2)) # 2
print(next(generator2)) # 3
print(next(generator2)) # 4
print(next(generator2)) # 5
九、其他
单下划线(_xxx):前面加一个下划线以单下划线开头的表示的是protected类型的变量,即保护类型只能允许其本身写子类D行访问,不能使用from xoxx import *的方式导入。
双下划线(__xxx):为私有属性
头尾双下划线(__xxx__):前后两个下滑线,魔法方法(属性),一般是python自有,开发者不要创建这类型的方法。
尾单下划线(xxx_):避免属性名与python关键字冲突。
super() 用于调用父类
函数、列表、字典每次定义都会开辟一块空间
def foo1():
pass
def foo2():
pass
print(foo1 is foo2) # False
print(id(foo1)) # 2643642119360
print(id(foo2)) # 2643642121088
print([] is []) # False
print({} is {}) # False
本文只用于个人学习与记录