动态绑定属性和方法 python是一种动态语言,而动态语言的类的属性和方法可以动态绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Student (object ): pass s = Student() s.name = 'Peter' print (s.name)def set_age (self, age ): self .age = age from types import MethodType s.set_age = MethodType(set_age, s) s.set_age(25 ) s.age>>> 25
这种动态绑定属性和方法,可以在程序运行过程中对实例添加属性和方法,但是只是对该实例生效
,同一个类创建的另一个实例是不生效的.
__solt__ 如果不想让别人随意动态绑定属性,可以使用__solt__
指定可以绑定的属性名,实例只可以绑定指定的属性.
1 2 3 4 5 6 7 8 9 10 class Student (object ): __slots__ = ('name' , 'age' ) >>> s = Student() >>> s.name = 'Michael' >>> s.age = 25 >>> s.score = 99 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'Student' object has no attribute 'score'
@property 属性装饰器,用于将类的方法
转换为属性
,从而实现属性的访问和设置时调用对应的方法.
主要适用场景有:
实现属性的访问控制: 将方法装饰为@property控制属性的访问权限(只读,只写,读写). 简化属性的访问: 将方法转为属性,就可以直接适用属性的访问方式(点号语法)访问方法 属性计算/验证: 在属性访问时执行特定的计算或逻辑,或实现验证 getter和setter: 类的外部公共接口,用来访问和修改属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Circle : def __init__ (self, radius ): self .radius = radius @property def diameter (self ): return self .radius * 2 @diameter.setter def diameter (self, value ): if not isinstance (value, int ): raise ValueError('diameter must be an integer!' ) if value < 0 or value > 100 : raise ValueError('diameter must between 0 ~ 100' ) self ._radius = value a = Circle(5 )print (a.diameter) a.diameter = -1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Student : @property def birth (self ): return self ._birth @birth.setter def birth (self, value ): self ._birth = value @property def age (self ): retuen 2015 - self ._birth
多重继承 一个子类可以通过多重继承同时获得多个父类的所有功能.
假设要实现4种动物:
Dog - 狗 Bat - 蝙蝠 Parrot - 鹦鹉 Ostrich - 鸵鸟 我们可以按照哺乳动物和鸟类来归类,可以设计出这样的类的层次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌───────────────┐ │ Animal │ └───────────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ Mammal │ │ Bird │ └─────────────┘ └─────────────┘ │ │ ┌─────┴──────┐ ┌─────┴──────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Bat │ │ Parrot │ │ Ostrich │ └─────────┘ └─────────┘ └─────────┘ └─────────┘
如果按能跑
和能飞
来划分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌───────────────┐ │ Animal │ └───────────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ Runnable │ │ Flyable │ └─────────────┘ └─────────────┘ │ │ ┌─────┴──────┐ ┌─────┴──────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Ostrich │ │ Parrot │ │ Bat │ └─────────┘ └─────────┘ └─────────┘ └─────────┘
如果合并两种划分方式,就要设计更多层次:
哺乳类: 能跑的哺乳类, 能飞的哺乳类 鸟类: 能跑的鸟类, 能飞的鸟类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌───────────────┐ │ Animal │ └───────────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ Mammal │ │ Bird │ └─────────────┘ └─────────────┘ │ │ ┌─────┴──────┐ ┌─────┴──────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ MRun │ │ MFly │ │ BRun │ │ BFly │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Bat │ │ Ostrich │ │ Parrot │ └─────────┘ └─────────┘ └─────────┘ └─────────┘
如果还要增加”宠物类”和”非宠物类”呢?层次将会越搞越多,类的数量也会呈指数级增长.此时就应该用到多重继承.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Animal (object ): pass class Mammal (Animal ): pass class Bird (Animal ): pass class Dog (Mammal ): pass class Bat (Mammal ): pass class Parrot (Bird ): pass class Ostrich (Bird ): pass class Runnable (object ): def run (self ): print ('Running...' )class Flyable (object ): def fly (self ): print ('Flying...' ) class Dog (Mammal, Runnable): pass class Bat (Mammal, Flyable): pass
MixIn 一般在设计类的继承关系时,通常都是单一继承,但是如果需要混入
额外功能时候,可以通过多重继承实现.这种设计称为MixIn.
MixIn的目的就是给一个类增加多个功能.在设计类的时候,我们优先考虑通过多重继承组合多个MixIn功能,而不是设计多层次的继承关系
为了更好地看出继承关系,一般我们会把需要加上的类名改为xxxMixIn
,MixIn类的定义通常不定义__init__
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class SpeakMixin : def speak (self ): print (f"I can speak." )class EatMixin : def eat (self ): print (f"I can eat." )class Animal : pass class Dog (Animal, SpeakMixin, EatMixin): pass dog = Dog() dog.speak() dog.eat()
python自带的很多库也使用了MixIn,比如TCPServer
和UDPServer
这两类网络服务,如果要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn
和ThreadingMixIn
提供.
1 2 class MyTCPServer (TCPServer, ForkingMixIn): pass
方法解析顺序(MRO) python中用于确定类方法调用顺序的算法.在多重继承下,当一个类实例调用一个方法时,MRO会决定应该使用哪个父类的实现.确保方法调用的确定性和一致性,避免命名冲突和歧异.
基本规则:
深度优先搜索: MRO从当前类开始,一次搜索父类和祖先类,直到找到要调用的方法 从左到右: 在每个类中,MRO会从左到右搜索其基类列表 线性化: MRO会将所有继承关系转换为一个线性顺序,从而避免循环引用和无限递归 具体步骤:
获取当前类的MRO属性: 每个类都有一个__mro__
属性,其中包含了该类的MRO列表 遍历MRO列表: 对于MRO列表中的每个类,检查,检查该类是否定义了要调用的方法 找到第一个定义该方法的类: 如果找到,则使用该类的实现 没有找到: raise AttributeError
异常 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import inspectclass A : pass class B (A ): pass class C (B ): pass class D (C, B): pass mro = inspect.getmro(D)print (mro) print (D.__mro__)
定制类 魔术方法 以双下划线开头和结尾
的特殊方法,用于扩展类的功能和行为.这些方法允许你控制类的实例化,属性访问,运算符重载,迭代等行为,从而实现更强大的类的设计.
常见的魔术方法及用途:
魔术方法 用途 __init__
初始化方法,用于创建类的实例时设置属性 __str__
字符串表示方法,用于将类实例转换成字符串 __repr__
表示方法,用于提供更详细的类实例表示 __iter__
迭代器方法,用于使类实例可用于for循环 __next__
迭代器方法,用于返回迭代的写一个元素 __getitem__
索引方法,用于获取类实例的元素 __setitem__
索引方法,用于设置类实例的元素 __add__
加法运算符重载方法,用于定义自定义加法运算 __sub__
减法运算符重载方法,用于定义自定义减法运算 __mul__
乘法运算符重载方法,用于定义自定义乘法运算 __eq__
等价运算符重载方法,用于定义自定义相等性检查 __call__
调用方法,用于使类实例可想函数一样调用
使用这些魔术方法来自定义
类的行为,就称为定制类,下面是一些例子
__init__(self, ...)
: 初始化实例。
1 2 3 4 class MyClass : def __init__ (self, attr1, attr2 ): self .attr1 = attr1 self .attr2 = attr2
__str__(self):
返回一个可读的字符串表示该类的一个实例。
1 2 3 4 5 6 class MyClass : def __init__ (self, value ): self .value = value def __str__ (self ): return f"MyClass({self.value} )"
__add__(self, other):
实现加法操作。注意,这个方法通常用于实现类与数字之间的相加。
1 2 3 4 5 6 class MyClass : def __init__ (self, value ): self .value = value def __add__ (self, other ): return MyClass(self .value + other)
__len__(self):
返回对象的长度(如字符串或列表的长度)。
1 2 3 4 5 6 class MyClass : def __init__ (self, value ): self .value = value def __len__ (self ): return len (self .value)
__eq__(self, other):
实现等于操作 ==
。通过实现这个方法,你定义了两个类的实例相等的标准。
1 2 3 4 5 6 class MyClass : def __init__ (self, value ): self .value = value def __eq__ (self, other ): return self .value == other.value
枚举类 枚举 python中通常使用大写字母
+下划线
来定义常量,python中的常量没有严格的限制,没有关键字声明,它在代码中还是可变的.当常量数量变多,声明和引用可能会变得麻烦或混乱,此时可以使用枚举类
来定义和管理这些常量.
枚举是一种数据类型,用于定义一组固定且有意义的值。想象一下,你想要表示一个星期中的某一天,你可能会用数字 1 到 7 来表示,但这样容易让人混淆。枚举允许你用更具描述性的名字来表示这些值,例如 MONDAY, TUESDAY, WEDNESDAY 等。
python中使用enum
模块来实现枚举类
.
优点:
代码可读性提升: 使用枚举类,代码更清晰易懂,因为每个值都有一个有意义的名字。类型安全: 枚举类定义了常量的类型,避免了使用错误的值。代码维护方便: 修改枚举值只需修改枚举类定义,无需修改使用该枚举类的代码。1 2 3 4 5 6 7 8 9 10 11 12 13 14 from enum import Enumclass Weekday (Enum ): MONDAY = 1 TUESDAY = 2 WEDNESDAY = 3 THURSDAY = 4 FRIDAY = 5 SATURDAY = 6 SUNDAY = 7 print (Weekday.MONDAY) print (Weekday.MONDAY.value)
枚举类可以定义方法,比如定义__str__
自定义输出
1 2 3 4 5 6 7 8 9 10 11 12 from enum import Enumclass TrafficLight (Enum ): RED = "stop" YELLOW = "caution" GREEN = True def __str__ (self ): return self .valueprint (TrafficLight.RED)
实际使用例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import requestsfrom enum import Enumclass HttpStatusCode (Enum ): OK = 200 CREATED = 201 BAD_REQUEST = 400 NOT_FOUND = 404 INTERNAL_SERVER_ERROR = 500 def handle_response (status_code ): if status_code == HttpStatusCode.OK: print ("Request successful!" ) elif status_code == HttpStatusCode.CREATED: print ("Resource created successfully!" ) elif status_code == HttpStatusCode.BAD_REQUEST: print ("Invalid request parameters." ) elif status_code == HttpStatusCode.NOT_FOUND: print ("Resource not found." ) elif status_code == HttpStatusCode.INTERNAL_SERVER_ERROR: print ("Server encountered an error." ) else : print (f"Unknown status code: {status_code} " ) response = requests.get('http://www.example.com' ) response_status_code = response.status_code handle_response(response_status_code)
元类与type 在python这种动态语言中,函数和类的定义不是编译时发生的,而是在运行时动态创建的.
比如写一个hello.py
模块
1 2 3 class Hello (object ): def hello (self, name='world' ): print (f'Hello {name} ' )
当python解释器载入hello
模块,会依次执行该模块的代码,结果就是动态创建一个Hello
的class对象
1 2 3 4 5 6 7 8 >>> from hello import Hello>>> h = Hello()>>> h.hello() Hello world>>> print (type (Hello)) <class 'type' >>>> print (type (h)) <class 'hello.Hello' >
可以看到Hello
是一个class,类型就是type
,h
是一个实例,也是一个class,类型是Hello
.
先来了解几个概念:
类: 面向对象编程中,类是对象的蓝图,包含了对象的属性(数据)和方法(行为). 实例: 类是模板,实例是根据模板创建的具体对象 元类: 元类是类的一种特殊类型,用于创建类,简单来说:类是用来创建对象的,元类用来创建类 type()
是一个内置函数,用来获取对象的类型信息,也可以用来动态创建类.
1 2 3 4 5 type (name, bases, attrs)
type()是动态类创建的工具或接口,而元类是这个接口背后的机制.
当我们使用type()创建动态类时,type()会调用python的元类系统.
python的默认元类就是type
,这意味着当我们使用type()创建类时,默认会使用type元类
这就解释了为什么Hello
这个class的类型是type.因为python默认是使用type(),基于type元类
来创建动态类的.
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况到。
定义 1 2 3 4 5 class MyMeta (type ): def __new__ (cls, name, bases, attrs ): return super ().__new__(cls, name, bases, attrs)
__new__()
方法是元类的关键方法,它负责创建新的类。cls
: 元类自身name
: 新类的名称bases
: 新类的父类列表attrs
: 新类需要包含的属性和方法字典1 2 3 4 5 6 7 8 9 return super ().__new__(cls, name, bases, attrs)''' 因此这行代码的的具体作用是: 1. 调用父类type的__new__()方法: 因为我们继承自type类,需要使用父类的机制来创建新类 2. 传递参数: 传递元类MyMeta自身(cls), 新类名称(name),新类父列表(bases)和新类属性和方法字典(attrs)到父类的__new__()方法中 3. 返回新类对象: 父类__new__()方法最终返回一个新的类对象 '''
使用 使用metaclass
参数来指定自定义元类创建类
1 2 3 class MyClass (metaclass=MyMeta): def __init__ (self, value ): self .value = value
MyClass
使用MyMeta
元类创建.MyMeta
的__new__()
方法将被调用,并能修改MyClass
的属性和行为.
实际使用 元类虽然不是最常见的编程技巧,但它在一些特定场景下能发挥强大的作用,提升代码的可维护性和可扩展性.
比如你在开发一个大型电商平台,需要管理各种商品类型,比如衣服,书籍,电子产品等,每个都有特定的属性和行为,比如衣服需要尺寸,颜色等属性,书记需要作者,出版社等
传统方式:
分别定义各种类,比如Clothing
,Book
,Electronics
每个类都有各自的属性和方法,代码架构会变大且难以维护
使用元类
定义一个元类ProductMeta
,用于创建商品类型的类
ProductMeta
可以接受商品类型的属性和方法作为参数,并根据这些参数动态创建类
这样,我们可以用一种通用的方式来定义商品类型,避免重复代码,并更容易添加新的商品类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class ProductMeta (type ): def __new__ (cls, name, bases, attrs ): attrs['get_product_info' ] = lambda self : f"Product name: {self.__class__.__name__} " return super ().__new__(cls, name, bases, attrs) class Product (metaclass=ProductMeta): def __init__ (self, name, price ): self .name = name self .price = price class Clothing (Product ): def __init__ (self, name, price, size, color ): super ().__inti__(name, price): self .size = size self .color = color class Book (Product ): def __init__ (self, name, price, author, publisher ): super ().__init__(name, price) self .author = author self .publisher = publisher clothing = Clothing("T-shirt" , 20 , "M" , "Red" ) book = Book("The Hitchhiker's Guide to the Galaxy" , 10 , "Douglas Adams" , "Pan Books" )print (clothing.get_product_info()) print (book.get_product_info())
这里,直接使用元类创建具体商品类也是可行的,但是中间添加一层Product中间类有以下优点:
代码组织性: 将Product
作为基类,可以将所有商品类型代码组织在一起,形成更清晰的层次结构,便于理解和维护 代码复用: Product
类可以包含所有商品类共有的属性和方法,提高代码复用性 扩展性: 如果将来需要添加新商品,只需要继承Product
类,并实现具体的属性和方法即可 类型安全: 所有商品类都继承Product
保证所有商品类都拥有共同的接口,提高代码的类型安全和可维护性