反射
Python 中,反射(Reflection)是一种能力,使得程序能够在运行时查看和修改自身的结构和行为。通过反射,您可以动态地访问和操作类的属性和方法,而无需在编写代码时确定它们的确切名称。这在某些情况下非常有用,例如在框架、库或插件系统中。
对对象的反射
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('牛老师',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__)
对类的反射
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'))
案例:基于反射的用户管理
使用反射前
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()
用了反射之后
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 方法
通过打印函数(方法)名确定
def func():
pass
print(func)
class A:
def func(self):
pass
print(A.func)
obj = A()
print(obj.func)
通过types模块验证
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))
静态方法是函数
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))
函数与方法的区别
那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。
函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
函数则跟对象无关。
方法中的数据则是隐式传递的。
方法可以操作类内部的数据。
方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中
双下方法
__init__
用于初始化类的实例,接收参数并设置实例属性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
__len__
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__
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方法,那么在打印对象 时,默认输出该方法的返回值。
class A:
def __init__(self):
pass
def __str__(self):
return '陈松'
a = A()
print(a)
print('%s' % a)
__repr__
如果一个类中定义了repr方法,那么在repr(对象) 时,默认输出该方法的返回值。
class A:
def __init__(self):
pass
def __repr__(self):
return '陈松'
a = A()
print(repr(a))
print('%r'%a)
__call__
对象后面加括号,触发执行。
注:构造方法new的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()
class Foo:
def __init__(self):
print('__init__')
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
__eq__
如果存在什么东西=什么,就会触发类中的__eq__
方法
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__()
方法会返回cls(cls指代当前类)的实例,然后该类的__init__()
方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入__new__()
方法中接收的位置参数和命名参数。
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)
单例模式
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++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用