**内容概览:**
1、反射
2、实例化对象的反射操作
3、类名的反射操作
4、其他 py 文件的反射操作
5、本py文件的反射操作
6、特殊双下划线方法
7、单例模式
8、item 系列
反射
反射有四个方法:hasattr、getattr、setattr、delattr,比较常用的是前两种,一般会结合起来用。
最最重要的一点:通过字符串去操作对象的属性和方法,是字符串形式!
什么对象可以用反射?
实例化对象、类、其他模块、本模块
只有以上四个才能使用,因为他们都能通过 . 的方式获取或调用,这也算是一种前提
class A:
name = "海绵宝宝"
def func(self):
print(666)
content = input("请输入:").strip()
ret = getattr(A, content)
print(ret)
运行结果一:
请输入:name
海绵宝宝
运行结果二:
请输入:func
<function A.func at 0x7f4bdc6710d0>
运行结果三:
请输入:123
Traceback (most recent call last):
File "test01.py", line 9, in <module>
ret = getattr(A, content)
AttributeError: type object 'A' has no attribute '123'
原因解析:报错提示类 A 里面没有这个属性
也就是说,只有用户输入的是字符串形式的属于类 A 的属性时才不会报错
实例化对象的反射操作
class A:
country = "中国"
area = "深圳"
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
print(666)
a = A("Jane", 18)
#对象的属性
print(a.name) # Jane
#注意这个变量名也要用字符串形式!
print(hasattr(a, "name")) # True
#不是这样用,而是字符串形式的属性名
print(hasattr(a, "Jane")) # False
#一般 hasattr 与 getattr 结合起来使用
if hasattr(a, "name"):
print(getattr(a, "name")) # Jane
#可以设置一个默认值,目的是防止程序报错
#如果没有该属性,就返回默认值
print(getattr(a, "sex", None)) # None
print(a.country) # 中国
print(getattr(a, "country")) # 中国
ret = getattr(a, "func")
#注意这里 ret() 相当于 func()
print(ret()) # 666
#给对象添加一个属性
setattr(a, "sex", "男")
print(a.sex) # 男
#删除对象的某个属性
delattr(a, "name")
print(a.name) # AttributeError: 'A' object has no attribute 'name'
类名的反射操作
class A:
country = "中国"
area = "深圳"
def __init__(self, name, age):
self.name = name
self.age = age
def func(self):
print(666)
# 获取类 A 的静态属性 country
print(getattr(A, "country")) # 中国
# 获取类 A 的静态属性 area
print(getattr(A, "area")) # 深圳
# 获取类A 的动态方法并执行
getattr(A, "func")(23) # 666
其他 py 文件的反射操作
1|假设有个 test02.py 文件,内容如下
flag = True
def func(a):
return a + 3
class B:
name_list = ["aaa", "bbb", "ccc"]
def __init__(self, name, sex):
self.name = name
self.sex = sex
def func(self):
print(666)
- 在本 py 文件中,不用反射的操作方法
import test02
print(test02.flag) # True
ret = test02.func
print(ret(10)) # 13
print(test02.B.name_list) # ['aaa', 'bbb', 'ccc']
obj = test02.B("barry", "男")
print(obj.name_list) # ['aaa', 'bbb', 'ccc']
- 在本 py 文件中,使用反射的操作方法
import test02
# 获取 test02 包中的 flag 变量对应的值
print(getattr(test02, "flag")) # True
# 执行 test02 包中的 func 方法
ret = getattr(test02, "func")(10)
print(ret) # 13
# 获取 test02 包中的类 B
print(getattr(test02, "B")) # <class 'test02.B'>
# 获取 test02 包中的类 B 的 name_list 属性的方式:
# 方式一:
print(getattr(test02, "B").name_list) # ['aaa', 'bbb', 'ccc']
# 方式二:
print(getattr(test02.B, "name_list")) # ['aaa', 'bbb', 'ccc']
# 执行 test02 包中的类 B 的 func 方法(同上两种方式)
getattr(test02, "B").func(111) # 666
getattr(test02.B, "func")(1) # 666
实例化对象
obj = getattr(test02, "B")("小明", "男")
# 获取实例化对象的属性 name
print(obj.name) # 小明
# 通过实例化对象获取到类 B 中的共享数据之静态属性: name_list
print(obj.name_list) # ['aaa', 'bbb', 'ccc']
# 通过实例化对象执行类 B 中的共享数据之动态方法: func()
obj.func() # 666
本py文件的反射操作:反射的主体是本文件
import sys
def func():
print(666)
ret = input("请输入: ").strip()
obj = sys.modules[__name__]
getattr(obj, ret)()
# 运行结果:只有输入 func 才不会报错
请输入: func
666
# 在本文件调用所有的函数
def func1():
print("in func1")
def func2():
print("in func2")
def func3():
print("in func3")
l1 = [func1, func2, func3]
for i in l1:
i()
# 运行结果:
in func1
in func2
in func3
# 要是有100个就不能这样了
import sys
l1 = ["func%s" % i for i in range(1, 4)]
print(l1) # ['func1', 'func2', 'func3']
obj = sys.modules[__name__]
for i in l1:
getattr(obj, i)()
# 运行结果:
in func1
in func2
in func3
特殊双下划线方法
1、项目中几乎不会自定义一个特殊双下划线方法,因为一般是给 Python 开发者在源码中使用
2、len 方法
class A:
def __init__(self, name, age):
self.name = name
self.age = age
self.sex = "男"
def __len__(self):
return len(self.__dict__)
a1 = A("蓬蓬", 18)
# 这里假如要计算实例化对象中 a1 的属性个性
# 我们知道,直接使用 len(a1) 是不行的,因为类没有 len 方法
# 这时候,可以在 A 里面添加一个 __len__ 方法,伪装它有 len()
print(len(a1)) # 3
# 通过这个例子可以得知,那些能使用 len()的数据类型内部肯定有 __len__方法
print(hash(a1)) # -9223371957293561519
# 这里的结果是实例化对象(object) 中有 __hash__ ,而不是A的
# 因为 A 里并没有添加一个 __hash__ 方法
- str 方法
class A:
def __init__(self, name, age):
self.name = name
self.age = age
self.sex = "男"
def __str__(self):
print(555)
return "abc"
a1 = A("蓬蓬", 18)
# 对一个对象打印时,自动执行 __str__ 方法
print(a1)
# 运行结果:
555
abc
- call 方法
class Foo:
def __init__(self):
print("实例化一个对象时自动执行 __init__ 方法")
def __call__(self, *args, **kwargs):
print('调用实例化对象时自动触发 __call__ 方法')
obj = Foo() # 实例化一个对象时自动执行 __init__ 方法
obj() # 调用实例化对象时自动触发 __call__ 方法
- new 构造方法
class A:
def __init__(self, name):
self.name = name
print("in A __init__")
a = A("小明")
# 实例化一个对象时,发生了三件事
# 1. 在内存中开辟了一个对象空间,注意是 obj (即 a )中 __new__ 开辟的
# 2. 自动执行 __init__ 方法,将空间传给 self
# 3. 在 __init__ 给对象封装属性,并返回给对象
# 也就是说,实例化一个对象的时候,首先执行了 __new__ 方法
# 然后执行了 __init__ 方法
class A:
def __init__(self, name):
self.name = name
print("in A __init__")
def __new__(cls, *args, **kwargs):
print(111)
a = A("小明")
print(a.name)
# 运行结果:
111
Traceback (most recent call last):
File "test01.py", line 11, in <module>
print(a.name)
AttributeError: 'NoneType' object has no attribute 'name'
# 原因分析:注意本来实例化一个对象 a 后
# a 的 __new__ 方法会在内存中创建一个空间
# 但是,这里类中有 __new__,此时只会执行 A 中的 __new__,即会打印 111
# 也就是说,内存中并没有 a1 的空间,因此也就没有 a1.name
# 最终,print(a1.name) 会报错
# 为了执行 obj(a1) 的 __new__,可以这样:
class A:
def __init__(self, name):
self.name = name
print("in A __init__")
def __new__(cls, *args, **kwargs):
print("in A __new__")
return object.__new__(cls)
a = A("小明")
print(a.name)
# 运行结果:
in A __new__
in A __init__
小明
# 根据上面所写实例化对象时的三个步骤,可以推导出以上打印结果
单例模式:一个类只能实例化一个对象
1、一般情况下,一个类可以有很多的实例化对象,但是每个的内存地址不一样
class A:
pass
ret1 = A()
ret2 = A()
ret3 = A()
print(ret1)
print(ret2)
print(ret3)
# 运行结果:
<__main__.A object at 0x7f33e2ccab70>
<__main__.A object at 0x7f33e12b1400>
<__main__.A object at 0x7f33e12b13c8>
- 单例模式示例
class A:
__instance = None
def __new__(cls, *args, **kwargs):
# cls 表示类本身, 这一句表示如果是第一次实例化对象
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance
ret1 = A()
ret2 = A()
ret3 = A()
print(ret1)
print(ret2)
print(ret3)
# 运行结果:
<__main__.A object at 0x7f2fc69493c8>
<__main__.A object at 0x7f2fc69493c8>
<__main__.A object at 0x7f2fc69493c8>
# 可以看出是在同一个内存地址,也就是说单例模式可以节省内存
# 比如有时很多地方要调用这个类,又必须调用实例对象,就用单例模式
- 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案
class A:
def __init__(self, name, age):
self.name =name
self.age = age
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance
ret1 = A("小明", 20)
ret2 = A("小花", 28)
ret3 = A("小白", 34)
print(ret1)
print(ret2)
print(ret3)
# 运行结果:
<__main__.A object at 0x7f0b8309f4a8>
<__main__.A object at 0x7f0b8309f4a8>
<__main__.A object at 0x7f0b8309f4a8>
item 系列
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
# 这里不能使用 self.key = value
# 因为那样相当于 self."age" = 18
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key] 时, 我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key 时, 我执行')
self.__dict__.pop(item)
f = Foo('abc')
f['age'] = 18
f['age1'] = 19
del f.age1 # del obj.key时,我执行
del f['age'] # del obj[key]时,我执行
f['name'] = '小明'
print(f.__dict__) # {'name': '小明'}