Skip to content

类和对象

在 python 中,用变量表示特征,用函数表示技能,
因而具有相同特征和技能的一类事物就是‘类’,
对象是则是这一类事物中具体的一个。
上一节中提到的异常类就是如此。

属性引用

python
class Person:  # 定义一个人类
    role = "person"  # 人的角色属性都是人

    def __init__(self, name):
        self.name = name  # 每一个角色都有自己的昵称;

    def walk(self):  # 人都可以走路,也就是有一个走路方法
        print("person is walking...")

实例化

类名()就等于在执行Person.__init__。事实上它还执行了Person.__new__

python
egg = Person("狗蛋")

执行完__init__()就会返回一个对象(这里的 egg)。
这个对象类似一个字典,存着属于这个人本身的一些属性和方法。

判断是否是实例

python
isinstance(obj, cls)

python3.10+ 允许使用|分隔多个 cls

访问对象属性

python
print(egg.name)

访问类属性,找不到对象属性的时候会根据访问链向 类属性,描述符,父类属性 查找(顺序远比这个复杂)

python
print(egg.role)
print(Person.role)

设置同名的对象属性不会影响到父类属性

调用类方法

python
egg.walk()

也可以用静态方法的方式调用类方法,直接实例占用形参self的位置

python
Person.walk(egg)

创建一个类就会创建一个类的名称空间,
用来存储类中定义的所有名字,这些名字称为类的属性。
静态方法就是直接定义在这个命名空间的函数。
它们的形参不含 self 和 cls,几乎与类和实例无关。

特殊属性

我们定义的类的属性到底存到哪里有两种方式查看

python
dir(类名)  # :查出的是一个名字列表
类名.__dict__  #:查出的是一个字典,key为属性名,value为属性值

特殊的类属性如下

python
类名.__name__  # 类的名字(字符串)
类名.__doc__  # 类的文档字符串
类名.__dict__  # 类的字典属性
类名.__module__  # 类定义所在的模块
类名.__slots__  # 属性元组/属性白名单(需要自己定义)
类名.__base__  # 类的第一个父类
类名.__bases__  # 类所有父类构成的元组
类名.__mro__  # 属性查找顺序,即类本身,类所有父类,object类,按照优先级构成的元组

实例化对象.__class__  # 实例对应的类(仅新式类中)

反射

python 面向对象中的反射:通过字符串的形式操作对象相关的属性。python 中的一切事物都是对象(都可以使用反射)

python
class Foo:
    f = "类的静态变量"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hi(self):
        print("hi,%s" % self.name)


obj = Foo("egon", 73)

# 检测是否含有某属性
print(hasattr(obj, "name"))
print(hasattr(obj, "say_hi"))

# 获取属性
n = getattr(obj, "name")
print(n)
func = getattr(obj, "say_hi")
func()

print(getattr(obj, "aaaaaaaa", "不存在啊"))  # 报错

# 设置属性
setattr(obj, "sb", True)
setattr(obj, "show_name", lambda self: self.name + "sb")
print(obj.__dict__)
print(obj.show_name(obj))

# 删除属性
delattr(obj, "age")
delattr(obj, "show_name")
delattr(obj, "show_name111")  # 不存在,则报错

print(obj.__dict__)

反射当前模块成员

python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys


def s1(): ...


def s2(): ...


this_module = sys.modules[__name__]

hasattr(this_module, "s1")
getattr(this_module, "s2")

依赖注入

当一个类使用其他类实例的时候 程序的耦合度将会大大提高

使用依赖注入的模式将依赖放入容器可以有效解耦

python 作为动态语言,接口类以及反射都可以轻松实现依赖注入

观察者模式

观察者模式(Observer)

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

描述符

描述符(descriptor) 是一种类

我们把实现了__get__()__set__()__delete__()中的其中任意一种方法的类称之为描述符

创建一个描述符

python
class Stu_descriptor:
    def __get__(self, instance, objtype):
        print("获取了val的值")
        return self.val

    def __set__(self, instance, val):
        print("修改了val的值")
        self.val = val

    def __delete__(self, instance):
        print("删除了val属性")
        del self.val

使用描述符来代理类属性

描述符是一种具有“捆绑行为”的对象属性

python
class Stu:
    age = Stu_descriptor()


stu = Stu()

stu = Stu()
stu.age = 12
print(stu.age)
del stu.age
stu = 10  # 自动回收内存

触发描述符的魔术方法

shell
>>> stu.age = 12
修改了val的值
>>> stu.age
获取了val的值
12
>>> del stu.age
删除了val属性
  • 数据描述符(data descriptor)

  • 非数据描述符(non-data descriptors)

数据描述符:定义了__set____get__ 方法的对象(property 是数据描述符)

非数据描述符:只定义了__get__ 方法的对象(staticmethod,classmethod 是非数据描述符)

描述符可以作为类内函数的装饰器将方法修饰成特殊属性或方法

与魔术方法__del__方法的不同

python
class Stu:
    def __del__(self):
        print("对象内存已被回收")


stu1 = Stu()
stu2 = Stu()
del stu1
# 对象内存已被回收
stu2 = 10
# 对象内存已被回收

__del__ 方法是对象内存被回收(包括自动回收)时触发,此方法一般无须定义。
因为 Python 是一门高级语言,程序员在使用时无需关心内存的分配和释放,
因为此工作都是交给 Python 解释器来执行,
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
更多的魔术方法将在后面介绍