Files
Cloud-book/Python/Python面向对象/反射与双下方法.md
2025-08-27 17:10:05 +08:00

345 lines
6.9 KiB
Markdown
Raw Permalink 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 中反射Reflection是一种能力使得程序能够在运行时查看和修改自身的结构和行为。通过反射您可以动态地访问和操作类的属性和方法而无需在编写代码时确定它们的确切名称。这在某些情况下非常有用例如在框架、库或插件系统中。
## 对对象的反射
```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("EaglesLab", 18)
# 检测是否含有某属性
print(hasattr(obj, "name"))
print(hasattr(obj, "say_hi"))
# 获取属性
print(getattr(obj, "name"))
func = getattr(obj, "say_hi")
func()
print(getattr(obj, "job", "不存在啊")) # 报错
# 设置属性
setattr(obj, "job", "teacher")
setattr(obj, "show_name", lambda self: self.name + " 真帅")
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
class User:
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
user = User()
while 1:
choose = input('>>>').strip()
if choose == 'login':
user.login()
elif choose == 'register':
user.register()
elif choose == 'save':
user.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++ 则取决于是否在类中。
# 双下方法
## `__init__`
用于初始化类的实例,接收参数并设置实例属性。
```python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
```
## `__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__` 方法,那么在 `print(obj)` 时,默认输出该方法的返回值。
```python
class A:
def __init__(self):
pass
def __str__(self):
return '陈松'
a = A()
print(a)
print('%s' % a)
```
## `__repr__`
如果一个类中定义了 `__repr__` 方法,那么在` repr(obj)` 时,默认输出该方法的返回值。
```python
class A:
def __init__(self):
pass
def __repr__(self):
return '陈松'
a = A()
print(repr(a))
print('%r'%a)
```
## `__call__`
通过 ` obj() ` 触发执行。
```python
class Foo:
def __init__(self):
print('__init__')
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
```
## `__eq__`
通过 `==` 触发类中的 `__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__`
在 Python 中,`__new__` 是一个控制对象创建过程的魔术方法,它比 `__init__` 更早执行。
- 优先级:`__new__` 是对象创建的第一步,负责生成实例;`__init__` 是第二步,负责初始化实例。
- 返回值:`__new__` 必须返回实例对象(否则 `__init__` 不会执行),而 `__init__` 无返回值。
- 静态方法:`__new__` 隐式作为静态方法存在,第一个参数是类本身 `cls`,而非实例 `self`
- ​​继承链​​:若未重写 `__new__`Python 会沿继承链调用父类的 `__new__`,直至 `object.__new__`
```python
class A:
def __new__(cls, *args, **kwargs):
print("in new function")
return object.__new__(A, *args, **kwargs) # 调用父类的 __new__ 创建实例并返回实例
def __init__(self):
self.x = 1
print("in init function")
a = A()
print(a.x)
```