Files
python-book/02.面向对象/06.反射与双下方法.md
2025-09-16 09:47:26 +08:00

645 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 反射与双下方法
## 反射
- python面向对象中的反射
- 指程序在**运行时**动态地获取对象(包括类、模块、实例等)的结构信息(如属性、方法、类型),并能动态地操作这些结构(如调用方法、修改属性、创建对象)的能力。简单来说,反射让程序能在运行时“自省”——了解自身的组成,并根据需要灵活调整行为。
- 反射的常见应用场景
- **动态配置加载**根据配置文件如JSON、YAML动态设置对象属性。例如通过`setattr`将配置中的`debug=True`映射到对象的`debug`属性。
- **插件化架构**:通过`importlib.import_module`动态加载插件模块,再用`getattr`获取插件类的实例并调用其方法。例如,电商系统可动态加载“限时抢购”“团购”等插件订单类。
- **通用工具函数**:编写适用于不同对象的通用工具。例如,`print_object_info`函数通过`dir`获取对象所有属性,再用`getattr`打印属性值。
- **ORM框架**通过反射将数据库表的列映射到Python类的属性。例如Django ORM通过反射获取模型的字段信息动态生成SQL语句。
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
**对对象的反射**
```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('张三',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
class Foo(object):
staticField = "test"
def __init__(self):
self.name = '张三'
def func(self):
return 'func'
@staticmethod
def bar():
return 'bar'
print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))
```
当前模块的反射
```python
import sys
def s1():
print('s1')
def s2():
print('s2')
this_module = sys.modules[__name__]
print(hasattr(this_module, 's1'))
print(getattr(this_module, 's2'))
```
其他模块的反射
程序目录:
module_test.py
test.py
当前文件:
test.py
```python
import module_test as obj
obj.test()
print(hasattr(obj,'test'))
getattr(obj,'test')()
```
举例:
使用反射前
```python
class User:
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
while 1:
choose = input('>>>').strip()
if choose == 'login':
obj = User()
obj.login()
elif choose == 'register':
obj = User()
obj.register()
elif choose == 'save':
obj = User()
obj.save()
```
用了反射之后
```python
class User:
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
user = User()
while 1:
choose = input('>>>').strip()
if hasattr(user, choose):
func = getattr(user, choose)
func()
else:
print('输入错误。。。。')
```
## 函数 vs 方法
### 通过打印函数(方法)名确定
```python
def func():
pass
print(func)
class A:
def func(self):
pass
print(A.func)
obj = A()
print(obj.func)
```
### 通过types模块验证
```python
from types import FunctionType
from types import MethodType
def func():
pass
class A:
def func(self):
pass
obj = A()
print(isinstance(func,FunctionType))
print(isinstance(A.func,FunctionType))
print(isinstance(obj.func,FunctionType))
print(isinstance(obj.func,MethodType))
```
### 静态方法是函数
```python
from types import FunctionType
from types import MethodType
class A:
def func(self):
pass
@classmethod
def func1(self):
pass
@staticmethod
def func2(self):
pass
obj = A()
# 静态方法其实是函数
print(isinstance(A.func2,FunctionType))
print(isinstance(obj.func2,FunctionType))
```
### 函数与方法的区别
那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。
1. 函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
2. 函数则跟对象无关。
3. 方法中的数据则是隐式传递的。
4. 方法可以操作类内部的数据。
5. 方法跟对象是关联的。如我们在用strip()方法是是不是都是要通过str对象调用比如我们有字符串s,然后s.strip()这样调用。是的strip()方法属于str对象。
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
在其他语言中如Java中只有方法C中只有函数C++么,则取决于是否在类中
## 双下方法
### `__len__`
```python
class B:
def __len__(self):
return 666
b = B()
print(len(b)) # len 一个对象就会触发 __len__方法。
class A:
def __init__(self):
self.a = 1
self.b = 2
def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))
```
### `__hash__`
```python
class A:
def __init__(self):
self.a = 1
self.b = 2
def __hash__(self):
return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))
```
### `__str__`
如果一个类中定义了__str__方法那么在打印 对象 时,默认输出该方法的返回值。
```python
class A:
def __init__(self):
pass
def __str__(self):
return '张三'
a = A()
print(a)
print('%s' % a)
```
### `__repr__`
如果一个类中定义了__repr__方法那么在repr(对象) 时,默认输出该方法的返回值。
```python
class A:
def __init__(self):
pass
def __repr__(self):
return '张三'
a = A()
print(repr(a))
print('%r'%a)
```
### `__call__`
对象后面加括号,触发执行。
构造方法__new__的执行是由创建对象触发的对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
```python
class Foo:
def __init__(self):
print('__init__')
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
```
### `__eq__`
```python
class A:
def __init__(self):
self.a = 1
self.b = 2
def __eq__(self,obj):
if self.a == obj.a and self.b == obj.b:
return True
a = A()
b = A()
print(a == b)
```
### `__del__`
析构方法,当对象在内存中被释放时,自动触发执行。
此方法一般无须定义因为Python是一门高级语言程序员在使用时无需关心内存的分配和释放因为此工作都是交给Python解释器来执行所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
### `__new__`
- `__new__()` 方法是在类准备将自身实例化时调用。
- `__new__()` 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
- 通常来说,新式类开始实例化时,`__new__()`方法会返回clscls指代当前类的实例然后该类的`__init__()`方法作为构造方法会接收这个实例即self作为自己的第一个参数然后依次传入`__new__()`方法中接收的位置参数和命名参数。
```python
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(A, *args, **kwargs)
a = A()
print(a.x)
```
单例模式
```python
class A:
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance
```
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
**【采用单例模式动机、原因】**
对于系统中的某些类来说只有一个实例很重要例如一个系统中可以存在多个打印任务但是只能有一个正在工作的任务一个系统只能有一个窗口管理器或文件系统一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化将弹出多个窗口如果这些窗口显示的内容完全一致则是重复对象浪费内存资源如果这些窗口显示的内容不一致则意味着在某一瞬间系统有多个状态与实际不符也会给用户带来误解不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
**【单例模式优缺点】**
**【优点】**
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
**【缺点】**
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象尤其在类库中定义的对象开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言只有单例类能够导致实例被取消分配因为它包含对该实例的私有引用。在某些语言中如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用
### `__item__`系列
```python
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key]=value
print('赋值成功')
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='mingzi'
print(f1.__dict__)
```
### 上下文管理器相关
`__enter__` `__exit__`
```python
class A:
def __init__(self, text):
self.text = text
def __enter__(self): # 开启上下文管理器对象时触发此方法
self.text = self.text + '您来啦'
return self # 将实例化的对象返回f1
def __exit__(self, exc_type, exc_val, exc_tb): # 执行完上下文管理器对象f1时触发此方法
self.text = self.text + '这就走啦'
with A('大爷') as f1:
print(f1.text)
print(f1.text)
```
自定义文件管理器
```python
class Diycontextor:
def __init__(self, name, mode):
self.name = name
self.mode = mode
def __enter__(self):
print("Hi enter here!!")
self.filehander = open(self.name, self.mode)
return self.filehander
def __exit__(self,*args):
print("Hi exit here")
self.filehander.close()
with Diycontextor('config', 'r') as f:
for i in f:
print(i.strip())
```
案例
```python
class StarkConfig:
def __init__(self, num):
self.num = num
def run(self):
self()
def __call__(self, *args, **kwargs):
print(self.num)
class RoleConfig(StarkConfig):
def __call__(self, *args, **kwargs):
print(345)
def __getitem__(self, item):
return self.num[item]
v1 = RoleConfig('abcedf')
v2 = StarkConfig('2333')
print(v1[3])
## print(v2[2])
v1.run()
```
```python
class UserInfo:
pass
class Department:
pass
class StarkConfig:
def __init__(self, num):
self.num = num
def changelist(self, request):
print(self.num, request)
def run(self):
self.changelist(999)
class RoleConfig(StarkConfig):
def changelist(self, request):
print(666, self.num)
class AdminSite:
def __init__(self):
self._registry = {}
def register(self, k, v):
self._registry[k] = v
site = AdminSite()
site.register(UserInfo, StarkConfig)
# 1
obj = site._registry[UserInfo]()
# 2
# obj = site._registry[UserInfo](100)
obj.run()
```
```python
class UserInfo:
pass
class Department:
pass
class StarkConfig:
def __init__(self,num):
self.num = num
def changelist(self,request):
print(self.num,request)
def run(self):
self.changelist(999)
class RoleConfig(StarkConfig):
def changelist(self,request):
print(666,self.num)
class AdminSite:
def __init__(self):
self._registry = {}
def register(self,k,v):
self._registry[k] = v(k)
site = AdminSite()
site.register(UserInfo,StarkConfig)
site.register(Department,RoleConfig)
for k,row in site._registry.items():
row.run()
```
```python
class A:
list_display = []
def get_list(self):
self.list_display.insert(0, 33)
return self.list_display
s1 = A()
print(s1.get_list())
```
```python
class A:
list_display = [1, 2, 3]
def __init__(self):
self.list_display = []
def get_list(self):
self.list_display.insert(0, 33)
return self.list_display
s1 = A()
print(s1.get_list())
```
```python
class A:
list_display = []
def get_list(self):
self.list_display.insert(0,33)
return self.list_display
class B(A):
list_display = [11,22]
s1 = A()
s2 = B()
print(s1.get_list())
print(s2.get_list())
```