《Python Cookbook》的作者David Beazley的课程PPT开源了,目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员,课程主题侧重于流行库和框架中使用的编程技术,主要目的是更好地理解 Python 语言本身,以便阅读他人的代码,并将新发现的知识应用到自己的项目中。内容组织的很棒,总共分为九个章节,我在阅读过程中顺便翻译整理下,用来查缺补漏了。翻译内容并非原版无对照翻译,有所增减,本篇是系列第四节。
感兴趣可前往原课件进行阅读👉 https://github.com/dabeaz-course/python-mastery/blob/main/PythonMastery.pdf
LLM应用全栈开发
字典
字典与实例
字典保存实例数据,每个实例都有自己的私有字典,如果创建了某个类的 100 个实例,则有 100 个保存数据的字典
字典与类
字典保存类的成员
实例与类
-
实例字典保存每个实例唯一的数据,而类字典保存所有实例共同共享的数据
-
__ class__ 属性引用回类
>>> s = Stock('GOOG', 100, 490.10) >>> s.__dict__ {'name':'GOOG','shares':100,'price':490.10 } >>> s.__class__ <class '__main__.Stock'> >>>
修改实例
修改对象的操作总是更新底层字典
>>> s = Stock('GOOG',100,490.10)
>>> s.__dict__
{'name':'GOOG', 'shares':100, 'price':490.10 }
>>> s.shares = 50
>>> s.date = '6/7/2007'
>>> s.__dict__
{ 'name':'GOOG', 'shares':50, 'price':490.10,
'date':'6/7/2007'}
>>> del s.shares
>>> s.__dict__
{ 'name':'GOOG', 'price':490.10, 'date':'6/7/2007'}
>>>
属性读取
首先检查实例__dict__
,如果没有找到,则查看类的__dict__
类继承
父类在每个类中存储为元组,扩展了用于查找属性的搜索过程
class A(B,C):
...
>>> A.__bases__
(<class '__main__.B'>,<class '__main__.C'>)
>>>
多重继承
MRO(Method Resolution Order)是指在面向对象编程中,确定类的方法调用顺序的算法。在Python中,MRO是通过C3线性化算法来实现的。C3线性化算法是一种基于拓扑排序的算法,用于解决多继承中方法调用的冲突问题,该算法保证了类的方法调用顺序满足一些重要的特性,例如保持局部顺序和首选父类等。
在Python中,类的继承是通过在类定义时指定基类来实现的。当一个类继承自多个基类时,Python会按照特定的顺序来解析方法调用。下面是一个示例,展示了MRO的工作原理:
class A:
def some_method(self):
print("A's method")
class B(A):
def some_method(self):
print("B's method")
class C(A):
def some_method(self):
print("C's method")
class D(B, C):
pass
d = D()
d.some_method()
在这个例子中,类D继承自类B和类C,而类B和类C都继承自类A。对于类D,其MRO的顺序可以通过以下方式获取:
print(D.__mro__)
输出结果为:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
从输出结果可以看出,方法调用的顺序是D -> B -> C -> A -> object。这意味着在类D中调用some_method()
方法时,首先会在类D中查找该方法,如果找不到,则按照MRO的顺序在类B、类C和类A中查找,直到找到该方法或者到达object类。
super()
super()
函数实际上是通过方法解析顺序(MRO)查找父类的方法,并在父类的上下文中执行该方法。这样可以实现子类对父类方法的扩展和重写,而不会丢失父类方法的功能。
class A(Base):
def spam(self):
Base.spam(self)
class A(Base):
def spam(self):
super().spam()
继承类时的三个原则
-
方法参数的要兼容:重写的方法必须在整个层次结构中具有兼容的签名,如果有不同的方法签名,使用关键字参数。
-
方法调用链必须以一个终止操作结束
-
尽量使用 super()
描述符协议
看着篇吧 Python 描述符简介,PPT 内容组织的有点乱
-
每当访问类上的属性时,都会检查该属性以查看它是否是一个看起来像所谓的“描述符”的对象
-
描述符包含有关实例、类和值的信息
-
self 是描述符本身,instance 是它正在操作的对象。
-
类的每个主要特征都是使用描述符来实现的
- 实例方法
- 静态方法(@staticmethod)
- 类方法(@classmethod)
- 属性(@property)
__ slots __
-
描述符提供了在运行时将实例和类连接在一起的粘合剂
描述符通常是作为类的属性来定义的,它们实现了一些特殊的方法,如
__get__
、__set__
和__delete__
。这些方法允许描述符拦截对属性的访问,并执行自定义的行为。class Descriptor: def __get__(self, instance, owner): print("Getting the value") return instance._value def __set__(self, instance, value): print("Setting the value") instance._value = value def __delete__(self, instance): print("Deleting the value") del instance._value class MyClass: attribute = Descriptor() def __init__(self, value): self._value = value obj = MyClass(10) print(obj.attribute) # Output: Getting the value 10 obj.attribute = 20 # Output: Setting the value print(obj.attribute) # Output: Getting the value 20 del obj.attribute # Output: Deleting the value
属性访问方法
-
类可以拦截属性访问
-
设置、删除和获取属性的特殊方法集
__getattribute__()
- 每次读取属性时调用
- 默认行为查找描述符、检查实例字典、检查基类(继承)等。
- 如果在完成所有这些步骤后仍找不到该属性,则会调用 __ getattr__
__getattr__()
- 一种故障安全方法。如果使用标准机制找不到属性,则调用
- 默认行为是引发 AttributeError
- 可自定义
__ setattr__()
- 每次设置属性时调用
- 默认行为检查描述符、将值存储在实例字典中等。
__ delattr__()
- 每次删除属性时调用
- 默认行为检查描述符并从实例字典中删除
自定义访问
- 类可以重新定义属性访问方法来实现自定义处理
- 最常见的应用是创建包装对象、代理和其他类似类型的对象
代理方式
保存对对象的内部引用;属性访问被重定向到持有的对象
class Proxy:
def __init__(self,obj):
self._obj = obj
def __getattr__(self,name):
print('getattr:', name)
return getattr(self._obj, name)
委派方式
有时用作继承的替代方式
class A:
def foo(self):
print('A.foo')
def bar(self):
print('A.bar')
class B:
def __init__(self):
self._a = A()
def bar(self):
print('B.bar')
self._a.bar()
def __getattr__(self, name):
return getattr(self._a, name)