六、流程控制语句
1、条件判断
1.1 if 语句
- if 语句的语法格式:
if 判断条件1:
执行语句1
elif 判断条件2:
执行语句2
....
else:
执行语句N
- if 语句的语法格式说明:
- 每个判断条件后面必须加冒号【:】
- 每个条件所执行的代码块必须往后缩进,缩进位置相同的执行语句组成一个代码块
- 在if语句中,只有一个if和else关键字,但允许有多个elif关键字,语句顺序必须是【if–elif–else】
- 一个简单的if 语句可以只有一个if 关键字
- 3.8新特性:在变量中赋值
# 在if语句中先对变量a赋值再判断
if a:=10==10
print("This is new style")
1.2 if 嵌套
- if 嵌套语句的语法格式:
if 判断条件1:
if 判断条件3:
执行语句3
elif 判断条件4:
执行语句4
else:
执行语句NS
elif 判断条件2:
执行语句2
else:
执行语句N
2、循环遍历
2.1 for 循环
- for 循环的语法格式:
for iterating in sequence:
print(iterating)
- 示例
# 循环字符串
print('------# 循环字符串-------')
str_1 = '我在学python'
result = []
for i in str_1:
result.append(i)
print(result)
# 循环列表
print('------# 循环列表-------')
list_1 = ['我', '正', '在', '学', 'Python']
result = []
for i in list_1:
result.append(i)
print(result)
# 循环字典
print('------# 循环字典-------')
dict_1 = {'key1': '我','key2': '在','key3': '学','key4': 'Python'}
result = []
for i in dict_1:
result.append(i)
print(result)
# 循环range对象
print('------# 循环range对象-------')
range_1 = range(10)
result = []
for i in range_1:
result.append(i)
print(result)
------# 循环字符串-------
['我', '在', '学', 'p', 'y', 't', 'h', 'o', 'n']
------# 循环列表-------
['我', '正', '在', '学', 'Python']
------# 循环字典-------
['key1', 'key2', 'key3', 'key4']
------# 循环range对象-------
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2.2 while 循环
- while 循环的语法格式:
while condition:
print(iterating)
2.3 终止循环
- break语句:终止整个循环,整个语句只能在循环中使用
2.4 跳过本次循环
- continue语句:终止当前循环,继续执行下一轮循环
2.5 空语句
- 关键字pass用于表示空语句,只是为了保证程序结果的完整性,没有任何实际性操作
3、三目运算符
- Python的三目运算符是if 语句的简化使用;语法如下:
# result代表三目运算符的运行结果
# 语句1是判断条件等于True的结果
# 语句2是判断条件等于False的结果
result = 执行语句1 if 判断条件 else 执行语句2
# 示例:
number = 10
result = 'This is True' if number >= 10 else 'This is False'
- 使用三目运算符的限制条件:
- 三目运算符必须遵循…if…else语句格式,在此基础上可以嵌套一个或多个三目运算符
- 三目运算符的执行语句只能是一行代码,如果要执行多行代码,只能使用if 语句
4、推导式
- 推导式是可以从一个数据序列构建另一个新的数据序列的结构体
- 推导式主要有列表推导式、集合推导式和字典推导式
# 示例:
# 列表推导式
result = [x for x in range(5)]
# 输出
[1,2,3,4]
# 集合推导式
result = {x for x in range(5)}
# 输出
{1,2,3,4}
# 字典推导式
result = {x: x for x in range(5)}
# 输出
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
- 推导式中增加if 判断
# 示例:
# 列表推导式
result = [x for x in range(10) if x > 5]
# 输出
[6, 7, 8, 9]
# 集合推导式
result = {x for x in range(10) if x > 5}
# 输出
{8, 9, 6, 7}
# 字典推导式
result = {x: x for x in range(10) if x > 5}
# 输出
{6: 6, 7: 7, 8: 8, 9: 9}
- 推导式和三目运算符结合
# 列表推导式
number = 10
result = [x for x in range(3)] if number > 10 else [x for x in range(5)]
# 输出
[0, 1, 2, 3, 4]
# 集合推导式
number = 10
result = {x for x in range(3)} if number > 10 else {x for x in range(5)}
# 输出
{0, 1, 2, 3, 4}
# 字典推导式
number = 10
result = {x: x for x in range(3)} if number > 10 else {x: x for x in range(5)}
{0: 0, 1: 1, 2: 2, 3: 3,4: 4}
七、函数
1、内置函数
- Python中定义了许多内置函数可以直接调用实现对应的功能
- 内置函数的使用官方文档:
https://docs.python.org/zh-cn/3.8/library/functions.html
2、自定义函数
- Python的函数定义方法是使用关键字def,函数定义的语法格式:
def 函数名称(参数1,参数2,...):
代码块
-
函数定义的语法规则:
- 函数必须使用关键字def 定义,关键字def 后面依次为函数名称,小括号( )和冒号 :
- 函数名要符合标识符规则
- 小括号里面可以定义函数参数,参数间使用逗号隔开
- 函数的代码块必须在关键字def 的位置上执行缩进处理
- 代码块中如果存在return 则有返回值,无则没有返回值
-
4种函数定义方式示例
# 没有参数和返回值
def func1():
print('没有参数和返回值')
# 有参数和返回值
def func1(x, y):
print('有参数和返回值')
# 没有参数和有返回值
def func1():
return '没有参数和有返回值'
# 有参数和有返回值
def func1(x, y):
return '有参数和有返回值'
- 标准的函数说明
def myDef(parameter):
'''
这是函数的功能说明
函数实现了xXX功能
参数parameter代表
返回值为空字符串
return ''
'''
3、函数参数
3.1 形参与实参
- 形参
- 形式上的参数,没有实际的值,在函数定义中设置的函数参数即为形参
- 实参
- 实际意义上的参数,参数被赋予实际的数值后会由形参转为实参
3.2 必选参数
- 必选参数是在函数调用过程中必须设置的函数,并且参数的 设置顺序不能改变
# 定义函数func1
# 参数x,y是必选参数
def func1(x, y):
result = x * 2 + y * 3
print(result)
3.3 默认参数
- 在定义函数的时候,对参数设置默认值,在函数调用的时候可无需再对已有默认值的参数赋值,该参数会使用默认值参与函数执行过程
- 不想使用默认值时,可以对参数重新赋值
- 当一个函数有必选参数和默认参数时,必选参数要排在默认参数之前,否则函数无法完成定义
# 定义函数func2
# 参数x是必选参数,y是默认参数
def func2(x, y=20):
result = x * 2 + y * 3
print(result)
3.4 关键字参数
- 在函数调用过程中可以明确设置参数名称并赋值
- 关键字参数在函数调用的过程中设置参数名称和参数值,如果全部参数按此规则,则无需按原有参数排序
- 函数参数要么按原有的排序方式、要么设置参数名称并赋值,这两种方法可以混合使用,但必须先顺序在使用参数名称赋值设置
# 定义函数func3
# 参数x,y是必选参数,参数z是默认参数
def func3(x, y, z=30):
result = x * 2 + y * 3 + z * 4
print(result)
if __name__ == '__main__':
func3(z=40, y=30, x=20)
3.5 可变参数
-
可变参数在定义的时候无须明确设置参数名称,在调用过程中可根据实际需要进行设置
- 可变参数可分为:参数列表(元组)、参数字典
-
参数列表是把多个参数的参数值放在一个列表里,再将整个列表传入函数中,定义过程如下:
# 定义函数 getPara
# 参数args 是可变参数的参数列表
def getPara(*args):
result = '-'.join(args)
print(f'参数列表有:{result}')
if __name__ == '__main__':
args = ['1', '2', '3']
getPara(*args)
-
可变参数的参数列表使用规则如下:
- 可变参数的参数列表在定义或调用过程中都要添加星号【 * 】,否则会默认为必选参数
- 参数列表的每个元素都没有明确的参数名,只有具体的参数值
- 在函数中使用参数列表无须添加星号【 * 】,可以通过列表索引值获取相应的参数值
-
可变参数的参数字典是把每个参数以键值对的格式写入字典,每个键值对的键代表参数名称,每个键值对的值代表参数值
- 定义过程如下:
# 定义函数 getPara
# 参数kwargs 是可变参数的参数字典
def getPara2(**kwargs):
keys = '-'.join(kwargs.keys())
values = '-'.join(kwargs.values())
print(f'参数字典的参数有:{keys},对应参数的值为:{values}')
if __name__ == '__main__':
kwargs = {'name': 'happy', 'age': '20'}
getPara2(**kwargs)
-
可变参数的参数字典使用规则如下:
- 可变参数的参数字典在定义或调用过程中都要添加星号【 ** 】,否则会默认为必选参数
- 参数字典的每个键值对代表一个参数,每个键值对的键代表参数名称,每个键值对的值代表参数值
- 在函数中使用参数字典无须添加双星号【 ** 】,将参数字典当做字典使用即可
-
在一个函数中,必选参数、默认参数、关键字参数和可变参数可以混合使用,但必须遵守参数排序要求:
- 必选参数–默认参数–可变参数(参数列表)–可变参数(参数字典)
3.6 设置数据类型
- 设置参数的数据类型是在参数名称后面添加冒号【 :】和数据类型
name:str
4、函数返回值
- 函数的参数是将外部数据传到函数内部使用,函数返回值是将函数内部的数据传递到外部使用
- 函数返回值可以由关键字return和关键字yield实现,两者有明显区别:
- return 是在返回结果的同时中断函数的执行
- yield 是返回结果并不中断函数的执行
# 定义函数
def my_return():
for i in range(5):
return i
# 定义函数
def my_yield():
for i in range(5):
yield i
if __name__ == '__main__':
result1 = my_return()
print('return数据类型是:', type(result1))
result2 = my_yield()
print('yield数据类型是:', type(result2))
for i in result2:
print('这是yield里面的数据:', i)
return数据类型是: <class 'int'>
yield数据类型是: <class 'generator'>
这是yield里面的数据: 0
这是yield里面的数据: 1
这是yield里面的数据: 2
这是yield里面的数据: 3
这是yield里面的数据: 4
- 函数返回值的数据类型在函数命名过程中设置,func( ) -> str:
- 设定的返回值类型和实际的返回值类型不一致时,不影响函数执行;但会有编码规范异常提示
def func0() -> str:
return 'string'
5、函数调用过程
- 函数调用的方式是使用【函数名+花括号】
def func1():
print('func1')
def func2(name):
print('func2')
# 调用函数func1
func1()
def func3(name):
print('func3')
# 调用函数func2
func2('happy')
6、变量的作用域
- 变量作用域主要分为全局变量和局部变量
- 两者区别:
- 在程序里定义的变量称为全局变量,在函数内部定义的变量称为局部变量
- 全局变量在所有作用域都可读,局部变量只能在本函数里可读
- 函数在读取变量时,优先速去函数本身的局部变量,再去读取全局变量
- 在函数里可以对变量使用关键字 global ,使变量定义成全局变量
7、递归函数
-
定义
- 把循环调用函数称为递归函数,大多数情况下,递归函数主要是调用自身函数
-
尾递归是指在函数返回时调用函数本身,并且return语句不能包含表达式
8、匿名函数
- 匿名函数(lambda表达式)是不再使用def语句这样的标准形式定义一个函数,使用lambda语句创建一个匿名函数
# 定义普通函数
def func(x, y):
return x * 2 + y * 3
# 定义匿名函数
f = lambda x, y: x * 2 + y * 3
-
匿名函数优点:
- 使用lambda可以省去定义函数的过程,让代码更加精简
- 对应函数使用较少或函数名较难的情况,使用lambda比def更为方便
-
使用匿名函数的规则:
- 只能有一个表达式,而且必须设有返回值
- 可以没有参数,也可以有一个或多个参数,并且不限制参数类型
- 返回值不能使用关键字return,匿名函数会自动将表达式的计算结果返回
- lamda语句后面设置参数名称,如果没有参数就无须填写,表达式在参数后面,并且使用冒号【 :】将表达式和参数隔开
9、偏函数
- 偏函数可以在函数的基础上修改默认参数的默认值并生成新的函数对象,只需要调用函数对象即可,这样无须多次设置默认参数
八、面向对象编程
1、对象与类
- 类
- 类是一种事物的抽象化,并不是具体指某一事物,二是泛指某一类事物
- 对象
- 对象是对某一类事物的具体化,即对类进行具体化和实例化
- 对象是从类实例化之后所产生的,但类与对象本质上代表同一个东西,只不过在实例化之前称为类,实例化之后称为对象
2、类的自定义与使用
- 面向对象的常用术语
- 类:用来描述具有相同属性和方法的对象集合,类定义了属性和方法,对象是类的实例。
- 对象:这是类实例化之后的产物。对象包括类属性和方法。
- 实例化(Instance):创建一个类的实例、类的具体对象,这是类实例化对象的过程。
- 类属性(类变量):类属性是类中定义的变量。
- 类方法:类中定义的函数方法。
- 方法重写:如果从父类继承的方法不能满足子类的需求,就可以对其进行改写,这个过程称为方法的覆盖(Override),也称为重写方法。
- 多态(Polymorphism):对不同类的对象使用同样的操作。
- 封装(Encapsulation):将类(对象)内部的方法隐藏起来,并使外部程序无法访问。
- 继承(Inheritance):即一个派生类(Derived Class)继承基类(Base Class)的属性和方法。
2.1 类的定义
- Python使用关键字class来定义类。其语法格式如下:
class Animals(object):
"""描述说明"""
name = '小黑'
def run(self):
n = self.name
print(f'{n} is running!')
- 定义过程说明如下:
- 在关键字class后面定义类的名称,一般情况下,类名称首个字母建议大写。
- 类名称的后面添加了小括号和object,代表当前定义的类继承object类。从Python 3开始,所有定义的类都默认继承object类,因此无须在类名称的后面添加小括号和object。
- 在类名称的末端必须设置英文冒号,代表下面已缩进的代码属于类的代码块。
- 类的第一行使用"“” “”"描述说明类所实现的功能、类属性的含义和类的方法功能等。
- 类属性的定义与变量的定义方式相同,在业界的规范下,类属性必须定义在类的方法前面。
- 类的方法定义与函数定义相似,但类的方法分为静态方法、类方法和实例方法,不同的方法有不同的定义方式。
- 在类的方法里面定义的变量如果没有添加self,该变量的作用域仅在当前的函数方法里有效,如函数方法run的变量n,只在函数方法里使用有效,如果在函数方法外使用就视为未定义变量。
2.2 类的内置属性和方法
- 类的内置属性和方法
__ new __:真正的类构造方法,用于生成实例化对象,重写new可以控制对象的生成过程,它一般很少用于普通的业务场景,更多地用于元类之中,因为可以在底层处理对象的生成过程。
__ init __:初始化方法,负责对实例化对象进行属性值初始化,此方法必须返回None,重写init可以控制对象的初始化过程。
__ str __:为了显式地显示对象的一些必要信息,方便查看和调试,它默认被print调用,默认所有类继承object类,object类的str用于输出对象的来源以及对应的内存地址。
__ repr __:作用与str相同,但它是被控制台输出时默认调用的。
__ call __:给对象提供被执行的能力,像函数一样,在本质上,函数也是对象,函数就是一个拥有call方法的对象,简单来说,它是把实例对象作为函数调用
__ del __:删除实例对象,用于当对象的引用计数为0时自动调用,它的应用场景分别为:(1)使用del减少对象引用计数至0,被垃圾回收处理时调用;(2)程序结束时调用
__ dict __:查看类所有的属性和方法,以字典格式表示,每个键值对的键代表类的属性或方法,每个键值对的值是属性值或方法的内存地址
__ doc __:查看类的注释说明,即获取类里面使用""""""添加的功能描述内容。
__ module __:获取类所在的模块,如果类导入其他模块,如模块A,那么className.module等于A。
__ slots __:控制类的内置属性,如果类中设置了slots,类实例化之后只允许访问和设置slots设置的属性,如果是slots之外的属性,就会提示异常。
__ class __:获取对象所对应的类名。
__ dir __与dir(对象名)等价,查看一个对象所有的属性和方法。
__ setitem __:如果在类中重新定义该方法,将类属性以字典格式进行数值设置,可以配合dict一同使用,比如a['name']= 'Tom'。
__ getitem __与setitem相似,但它是将类属性以字典格式进行访问,比如name=a['name']。
__ delitem __与setitem相似,但它是以删除字典某个键值对的方式来删除某个类属性的,比如del a['name'],使用关键字del删除某个类属性将会触发delitem
__ getattr __:获取某个属性的属性值的时候触发,如age=a.age,但是访问不存在的属性会提示异常,如a.city='shenzhen',如果重新定义了getattr,而没有任何代码(只有pass),那么所有不存在的属性值都是None而不会报错
__ setattr __:为某个属性设置属性值的时候触发,如设置a.age=10将会触发setattr
__ delattr __:删除某个属性的时候触发,如del a.age将会触发delattr
__ getattribute __:属性访问截断器,即访问属性时,这个方法会把访问行为截断,并优先执行此方法中的代码,此方法应该在属性查找顺序中优先级最高
__ enter __和exit可以让我们对一个对象使用with方法来处理工作前的准备,以及工作之后的清扫行为
2.3 静态方法、类方法和实例方法
- 类的方法划分为3种:静态方法、类方法和实例方法
class Animals(object):
"""
描述说明
"""
@staticmethod
def eat(name):
"""
静态方法示例
:param name:
:return:
"""
print('This is staticmethod')
@classmethod
def sleep(cls):
"""
类方法示例
:return:
"""
print('This is classmethod')
def run(self):
"""
实例方法示例
:return:
"""
print('This is instancemethod')
-
静态方法:
- 在定义的时候,在方法名的上一行使用@staticmethod装饰器标注为静态方法
- 它的第一个参数不需要设置self、cls
- 静态方法不需要实例化就能直接使用,也可以在实例化后使用
-
类方法
- 在定义的时候,在方法名的上一行使用@classmethod装饰器标注为类方法
- 它的第一个参数不需要设置self,但是必须设置cls
- 类方法不需要实例化就能直接使用,也可以在实例化后使用
-
实例方法
- 在定义的时候,类方法的第一个参数必须设置self
- 实例方法必须实例化之后才能使用
2.4 类的property属性
- 在类的实例方法上添加@property属性,它能把类的实例方法以类属性的形式使用
class Geometry:
"""
计算圆的周长
"""
@property
def compute(self):
perimeter = (self.width + self.length) * 2
return f'周长为:{perimeter}'
@compute.setter
def set_width(self, width):
self.width = width
@compute.setter
def set_length(self, length):
self.length = length
@compute.getter
def compute_half(self):
perimeter_half = self.width + self.length
return f'周长的一半为:{perimeter_half}'
g = Geometry()
g.set_length = 4
g.set_width = 4
print(g.compute)
print(g.compute_half)
2.5 类的实例化与使用
- 类的实例化
class Animals:
"""
内容描述
"""
# 类的实例化
a = Animals()
- 实例化对象调用类的方法和属性
# 类的实例化
a = Animals()
# 调用类属性
a.name
# 调用静态方法
Animals.run()
# 调用类方法
Animals.runs()
# 调用实例方法
a.run()
# 调用带property属性的方法
a.set_eats = 'Apples'
-
类属性调用说明
- 类属性支持实例化对象和未实例化的类进行调用
- 实例化之前属性值以类定义时的值为准,实例化成对象后以修改后的值为准
-
静态方法调用说明
- 支持实例化对象和未实例化的类进行调用
- 一般情况下都是使用未实例化的类进行调用
-
类方法调用说明
- 支持实例化对象和未实例化的类进行调用
- 建议是使用未实例化的类进行调用
-
实例方法调用说明
- 只能由实例化对象调用
-
带property属性的方法调用说明
- 支持实例化对象和未实例化的类进行调用
- 未实例化的类是可以调用property的setter属性赋值,调用getter属性时只能获取property对象的内存地址
- 只有当类实例化之后,并由实例化对象调用才能正常使用property属性的方法
-
开发规范
- 静态方法和类方法应由未实例化的类调用
- 类属性、实例方法和带property属性的方法应由已实例化的对象调用
2.6 动态添加类的属性和方法
- Python允许在类中动态添加属性和方法,实现过程如下:
- 在类名称后面使用实心点设置属性名称或方法名称
def jump():
print('This is jumping')
def doing(self):
print('This is running')
@classmethod
def sleeping(cls):
print('This is sleeping')
@staticmethod
def speaking():
print('This is speaking')
class Animals:
pass
if __name__ == '__main__':
# 实例化之前动态添加属性和方法
Animals.name = '小白'
Animals.doing = doing
Animals.sleeping = sleeping
Animals.speaking = speaking
# 实例化Animals
a = Animals()
# 类实例化之后,动态添加属性和方法
a.jump = jump
# 调用属性和方法
print(a.name)
a.doing()
a.sleeping()
a.speaking()
a.jump()
- 使用内置函数setattr( )实现动态添加属性和方法
if __name__ == '__main__':
# 实例化之前动态添加属性和方法
setattr(Animals,'name','小白')
setattr(Animals,'doing',doing)
setattr(Animals,'sleeping',sleeping)
setattr(Animals,'speaking',speaking)
# 实例化Animals
a = Animals()
# 类实例化之后,动态添加属性和方法
setattr(a,'jump',jump)
# 调用属性和方法
print(a.name)
a.doing()
a.sleeping()
a.speaking()
a.jump()
-
内置函数setattr( )三个参数说明
- 第一个参数代表任意对象
- 第二个参数代表对象的属性或方法名称
- 第三个参数代表属性值或方法的定义过程
-
动态添加属性或方法详细说明:
- 类方法和静态方法只能在类实例化之前添加到类里面
- 类属性和实例方法可以在类实例化前后添加
2.7 内置属性: __ slots __
- __ slots __说明
- 为了避免毫无节制地在实例化对象中动态添加类属性和实例对象,可使用__ slots __属性限制;它以元组或列表格式表示,只有元组或列表里面的元素才能作为动态添加的属性或方法的名称
- __ slots __只对当前的类起作用,它对派生类不起任何作用
3、类的封装
- 类的封装是指类的属性(变量)和方法封装在该类内,只有该类中的成员才可以使用和访问,在类的外部是无法使用和访问的
- 被封装的变量与方法被称为该类的私有变量与私有方法
- 在定义属性和方法的时候将它们名称前面两位字符使用单下划线定义
# 定义私有属性
__name = '小白'
# 定义私有方法
def __run(self):
print('This is running')
- 定义类的私有变量和私有方法的三种方式
- 使用property属性装饰类的实例方法,使实例方法以类属性的方式访问和调用
- 对类的属性和方法名称前面两位字符使用下划线定义,在调用过程中通过 【_className__xxx】访问
- 对类的属性和方法名称前面一位字符使用下划线定义
4、类的继承
- 类的继承是新类继承旧类的属性与方法,这种行为称为派生子类
- 类的继承是在类名称后面使用小括号,在小括号里面写入一个或多个基类的名称,如果要继承多个基类,每个基类之间使用逗号隔开
# 类的继承
class ClassName(Class1,Class2, ...)
"""
ClassName 派生类名
Class1,Class2 基类名称
"""
pass
-
继承分为单继承和多重继承,单一继承是指一个类只能继承某一个类,多重继承是指一个类继承两个或两个以上不同的类
-
当派生类定义的方法名称与基类的方法名称相同时,视为对基类方法进行重写,这时执行的是派生类定义的方法
5、内置函数:super( )
- 在内置函数super( )后面使用实心点连接基类的方法,就可以调用基类中的该方法
- 内置函数super( )解决多继承通过MRO规则
6、类的多态
- 类的多态是指类可以有多个名称相同、参数类型不同的函数方法
- 在一个类里面,只要在函数方法里面使用可变参数和关键字即可实现类的多态,因为可变参数和关键字参数不会限制参数的数量和数据类型
7、动态创建类
- 内置函数type( )可以查看数据类型,还可以动态创建类
# 内置函数type()动态创建类的语法
type(类名,基类(父类,) (以元组格式表示),属性和方法(以字典格式表示))
class Animals:
colour = 'red'
def doing(self):
print('Animals')
if __name__ == '__main__':
def run(self, name):
print(f'{name} is running')
def doing(self):
print('Animals')
# 动态创建Cat类
cat = type('Cat', (Animals,), {'colour': 'red', 'doing': doing, 'run': run})
# 实例化Cat类
c = cat()
print(c.colour)
c.doing()
c.run('小白')
- 说明
- 如果要为类设置函数方法,就必须实现定义好相应的函数方法
- 如果是类方法、静态方法、实例方法或者带property属性的方法,它们的定义方式与Class关键字定义的时候相同
- 定义好的方法以key-value传入type,字典的键值对只需传入方法名称即可无须添加小括号
8、创建类的类:元类
- 元类:创建类的类
- Python中的内置函数type( )是一个元类,它用来创建所有的元类
# 自定义元类
class MyMetaClass(type):
def speak(cls):
pass
def __new__(cls,name,bases,attrs):
"""
cls:代表自身类
name:代表类名
bases:代表类继承的所有基类,以元组的格式表示
attrs:代表类的所有属性和方法,以字典格式表示
"""
attrs['name'] = 'This is MetaClass'
attrs['speak'] = cls.speak
return super().__new__(cls,name,bases,attrs)
class Person(metaclass = MyMetaClass)
"""
定义Person类,并由元类MyMetaClass创建
"""
pass
-
自定义元类:
- 元类的定义和类的定义相似,只不过元类是继承type类,而不是继承object类
-
元类还可以使用函数的方式表示,使用这种方式表示是必须设置3个参数【name、bases、attrs】
# 使用函数的方式表示元类
def MyMetaClass(name,bases,attrs):
"""
name:代表类名
bases:代表类继承的所有基类,以元组的格式表示
attrs:代表类的所有属性和方法,以字典格式表示
"""
attrs['name'] = 'This is MetaClass'
attrs['speak'] = speak
return tpye(name,bases,attrs)
class Person(metaclass = MyMetaClass)
"""
定义Person类,并由元类MyMetaClass创建
"""
pass
- 自定义元类能够以函数方法的形式表示,是因为内置函数type( )能够动态创建类
- 本质:无论自定义元类是以类的形式还是函数方法的形式表示,它都是在type类或内置方法type( )的基础上进行继承和扩展