马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
x
本帖最后由 15271953841 于 2024-4-20 06:52 编辑
元类的应用
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec 7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
一、对类动手动脚,给所有的类添加一个作者的属性
class MetaC(type): (创建一个元类,继承自type)
def __new__(mcls,name,bases,attrs): (类完成定义的时候,它会执行元类的__new__方法)
attrs["author"] = "FishC" (类的属性里添加一对键值对)
return type.__new__(mcls,name,bases,attrs)
class C(metaclass=MetaC):
pass
class D(metaclass=MetaC):
pass
c = C()
d = D()
c.author
'FishC'
d.author
'FishC'
元类,两种方法都可以实现添盐加醋,因为__new__和__init__都是在类完成定义的时候被调用的。
class MetaC(type):
def __init__(cls,name,bases,attrs):
cls.author = "FishC"
return type.__init__(cls,name,bases,attrs)
class C(metaclass=MetaC):
pass
class D(metaclass=MetaC):
pass
c = C()
d = D()
c.author
'FishC'
d.author
'FishC'
二、对类名的定义规范做限制,比如:让类名支持大写字母开头,小写报错
class MetaC(type):
def __init__(cls,name,bases,attrs):
if not name.istitle():
raise TypeError("类名必须是大写字母开头~")
type.__init__(cls,name,bases,attrs)
class mycls(metaclass=MetaC):
pass
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
class mycls(metaclass=MetaC):
File "<pyshell#28>", line 4, in __init__
raise TypeError("类名必须是大写字母开头~")
TypeError: 类名必须是大写字母开头~
class Mycls(metaclass=MetaC):
pass
三、修改对象的属性值,比如:把对象的所有字符串属性值全部变成大写
class MetaC(type):
def __call__(cls,*args,**kwargs): (__call__影响类实例化的过程,对类的属性,把它在实例化过程的传入的属性值变成大写,而不是改这个类属性,改的是属性值)
new_args = [each.upper() for each in args if isinstance(each,str)] (参考第19课时最后的列表高阶表达式,其中的isinstance()函数参考第53课时)
return type.__call__(cls,*new_args,**kwargs)
class C(metaclass=MetaC):
def __init__(self,name):
self.name = name
c =C("FishC")
c.name
'FISHC'
四、限制类实例化时的传参方式,比如:要求类在实例化对象时传入的参数只能够通过关键字参数传参,如果传入的位置参数,那么报错
class MetaC(type):
def __call__(cls,*args,**kwargs):
if args:
raise TypeError("仅支持关键字参数")
return type.__call__(cls,*args,**kwargs)
class C(metaclass=MetaC):
def __init__(self,name):
self.name = name
c = C("FishC")
Traceback (most recent call last):
File "<pyshell#56>", line 1, in <module>
c = C("FishC")
File "<pyshell#51>", line 4, in __call__
raise TypeError("仅支持关键字参数")
TypeError: 仅支持关键字参数
c = C(name="FishC")
c.name
'FishC'
五、禁止一个类被实例化
class NoInstances(type):
def __call__(cls,*args,**kwargs):
raise TypeError("该类不允许直接实例化对象!")
class C(metaclass=NoInstances):
pass
c = C()
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
c = C()
File "<pyshell#3>", line 3, in __call__
raise TypeError("该类不允许直接实例化对象!")
TypeError: 该类不允许直接实例化对象!
class C(metaclass= NoInstances):
@staticmethod
def static_ok():
print("静态方法直接访问是允许的~")
C.static_ok()
静态方法直接访问是允许的~
c = C()
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
c = C()
File "<pyshell#3>", line 3, in __call__
raise TypeError("该类不允许直接实例化对象!")
TypeError: 该类不允许直接实例化对象!
class C(metaclass=NoInstances):
@classmethod
def class_ok(cls):
print("类方法直接访问是允许的~")
class C(metaclass=NoInstances):
@classmethod
def class_ok(cls): (类方法要传入一个类)
print("类方法直接访问是允许的~")
C.class_ok()
类方法直接访问是允许的~
六、只允许实例化一个对象,就是说,我们让这个类把它给多个实例,但其实它背后那个对象都是同一个,“计划生育”
class SimpleInstance(type):
def __init__(cls,*args,**kwargs):
cls.__instance = None (两个__表示私有变量,参考第58课时)
type.__init__(cls,*args,**kwargs)
def __call__(cls,*args,**kwargs):
if cls.__instance is None:
cls.__instance = type.__call__(cls,*args,**kwargs)
return cls.__instance
else:
return cls.__instance
class C(metaclass=SimpleInstance):
pass
c1 = C()
c2 = C()
c1 is c2
True
dir(C)
['_SimpleInstance__instance', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
|