100 道 Python 经典面试题超强汇总(三):Python 对象内部

《Python Cookbook》的作者David Beazley的课程PPT开源了,目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员,课程主题侧重于流行库和框架中使用的编程技术,主要目的是更好地理解 Python 语言本身,以便阅读他人的代码,并将新发现的知识应用到自己的项目中。内容组织的很棒,总共分为九个章节,我在阅读过程中顺便翻译整理下,用来查缺补漏了。翻译内容并非原版无对照翻译,有所增减,本篇是系列第四节。

感兴趣可前往原课件进行阅读 https://github.com/dabeaz-course/python-mastery/blob/main/PythonMastery.pdf

LLM应用全栈开发

字典

字典与实例

字典保存实例数据,每个实例都有自己的私有字典,如果创建了某个类的 100 个实例,则有 100 个保存数据的字典

100 道 Python 经典面试题超强汇总(三):Python 对象内部_第1张图片

字典与类

字典保存类的成员

100 道 Python 经典面试题超强汇总(三):Python 对象内部_第2张图片

实例与类
  • 实例字典保存每个实例唯一的数据,而类字典保存所有实例共同共享的数据

  • __ class__ 属性引用回类

    >>> s = Stock('GOOG', 100, 490.10)
    >>> s.__dict__
    {'name':'GOOG','shares':100,'price':490.10 }
    >>> s.__class__
    <class '__main__.Stock'>
    >>>
    

    100 道 Python 经典面试题超强汇总(三):Python 对象内部_第3张图片

修改实例

修改对象的操作总是更新底层字典

>>> 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__

100 道 Python 经典面试题超强汇总(三):Python 对象内部_第4张图片

类继承

父类在每个类中存储为元组,扩展了用于查找属性的搜索过程

class A(B,C):
		...
>>> A.__bases__
(,)
>>>

多重继承

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__)

输出结果为:

(, , , , )

从输出结果可以看出,方法调用的顺序是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()

100 道 Python 经典面试题超强汇总(三):Python 对象内部_第5张图片

继承类时的三个原则
  • 方法参数的要兼容:重写的方法必须在整个层次结构中具有兼容的签名,如果有不同的方法签名,使用关键字参数。

  • 方法调用链必须以一个终止操作结束

    image-20230812155302671

  • 尽量使用 super()

描述符协议

看着篇吧 Python 描述符简介,PPT 内容组织的有点乱

  • 每当访问类上的属性时,都会检查该属性以查看它是否是一个看起来像所谓的“描述符”的对象

  • 描述符包含有关实例、类和值的信息

  • self 是描述符本身,instance 是它正在操作的对象。

    100 道 Python 经典面试题超强汇总(三):Python 对象内部_第6张图片

  • 类的每个主要特征都是使用描述符来实现的

    • 实例方法
    • 静态方法(@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
    

属性访问方法

  • 类可以拦截属性访问

  • 设置、删除和获取属性的特殊方法集

    100 道 Python 经典面试题超强汇总(三):Python 对象内部_第7张图片

__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)

你可能感兴趣的:(Python语言特性,python,开发语言,人工智能,后端,AI编程,面试,django)