类和对象
在 python 中,用变量表示特征,用函数表示技能,
因而具有相同特征和技能的一类事物就是‘类’,
对象是则是这一类事物中具体的一个。
上一节中提到的异常类就是如此。
属性引用
class Person: # 定义一个人类
role = "person" # 人的角色属性都是人
def __init__(self, name):
self.name = name # 每一个角色都有自己的昵称;
def walk(self): # 人都可以走路,也就是有一个走路方法
print("person is walking...")
实例化
类名()就等于在执行Person.__init__
。事实上它还执行了Person.__new__
egg = Person("狗蛋")
执行完__init__()
就会返回一个对象(这里的 egg)。
这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
判断是否是实例
isinstance(obj, cls)
python3.10+ 允许使用|
分隔多个 cls
访问对象属性
print(egg.name)
访问类属性,找不到对象属性的时候会根据访问链向 类属性,描述符,父类属性 查找(顺序远比这个复杂)
print(egg.role)
print(Person.role)
设置同名的对象属性不会影响到父类属性
调用类方法
egg.walk()
也可以用静态方法的方式调用类方法,直接实例占用形参self
的位置
Person.walk(egg)
创建一个类就会创建一个类的名称空间,
用来存储类中定义的所有名字,这些名字称为类的属性。
静态方法就是直接定义在这个命名空间的函数。
它们的形参不含 self 和 cls,几乎与类和实例无关。
特殊属性
我们定义的类的属性到底存到哪里有两种方式查看
dir(类名) # :查出的是一个名字列表
类名.__dict__ #:查出的是一个字典,key为属性名,value为属性值
特殊的类属性如下
类名.__name__ # 类的名字(字符串)
类名.__doc__ # 类的文档字符串
类名.__dict__ # 类的字典属性
类名.__module__ # 类定义所在的模块
类名.__slots__ # 属性元组/属性白名单(需要自己定义)
类名.__base__ # 类的第一个父类
类名.__bases__ # 类所有父类构成的元组
类名.__mro__ # 属性查找顺序,即类本身,类所有父类,object类,按照优先级构成的元组
实例化对象.__class__ # 实例对应的类(仅新式类中)
反射
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__)
反射当前模块成员
#!/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__()
中的其中任意一种方法的类称之为描述符
创建一个描述符
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
使用描述符来代理类属性
描述符是一种具有“捆绑行为”的对象属性
class Stu:
age = Stu_descriptor()
stu = Stu()
stu = Stu()
stu.age = 12
print(stu.age)
del stu.age
stu = 10 # 自动回收内存
触发描述符的魔术方法
>>> stu.age = 12
修改了val的值
>>> stu.age
获取了val的值
12
>>> del stu.age
删除了val属性
数据描述符:定义了__set__
和__get__
方法的对象(property 是数据描述符)
非数据描述符:只定义了__get__
方法的对象(staticmethod,classmethod 是非数据描述符)
描述符可以作为类内函数的装饰器将方法修饰成特殊属性或方法
与魔术方法__del__
方法的不同
class Stu:
def __del__(self):
print("对象内存已被回收")
stu1 = Stu()
stu2 = Stu()
del stu1
# 对象内存已被回收
stu2 = 10
# 对象内存已被回收
__del__
方法是对象内存被回收(包括自动回收)时触发,此方法一般无须定义。
因为 Python 是一门高级语言,程序员在使用时无需关心内存的分配和释放,
因为此工作都是交给 Python 解释器来执行,
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
更多的魔术方法将在后面介绍