元类-练习
一. 在元类中控制把自定义类的数据属性都变成大写(提示: 内置数据属性不修改)
"""
思路: 明白包含类的名称空间在自定义元类中只有__init__方法和__new__方法. 而__init__方法只能控制类的产生或者对类添加新的属性. 因此我们这里用__new__方法
参数介绍:
__new__方法的第一个参数是自定义元类本身cls
__new__方法的*args, **kwargs其中只有args有值. 其中args拿到的是(类名class_name, 类继承的基类class_bases, 类的名称空间class_dict),
返回值: __new__方法必须返回一个对象(自定的类). 因为本题是争类的数据属性的修改, 所以可以对类的名称空间class_dict进程操作, 操作完毕以后再创建的新对象(自定义的类)的class_dict是更新过后的calss_dict.
"""
class MyMeta(type):
# cls=MyMeta args=('School', (), {执行类体代码产生的名称空间}) kwargs={}
def __new__(cls, *args, **kwargs):
class_name, class_bases, class_dict = args
# 判断:
# 1) 如果属性值是可被掉用的则是函数属性, 不是则是数据属性
# 2) 或者如果是以__开头的属性则是内置的属性, 不是则是自定义的属性
class_dict = {attr if callable(class_dict[attr]) or attr.startswith('__') else attr.upper(): value for attr, value in class_dict.items()}
return super().__new__(cls, class_name, class_bases, class_dict)
# School = MyMeta('School', (), {执行类体代码产生的名称空间})
class School(metaclass=MyMeta):
school_name = '老男孩'
def func(self):
pass
print(School.__dict__)
二. 在元类中控制自定义的类无需`__init__方法
'''
思路: 明白调用对象(自定义的类)就是触发自定义元类的__call__方法.
参数介绍:
__call__方法的第一个参数self是被调用对象(自定义的类),
__call__方法的接下来的*args, **kwargs会拿到被调用对象(自定义的类)时括号内的值.
'''
class MyMeta(type):
# self=People args=() kwargs={'name': 'egon', ....}
def __call__(self, *args, **kwargs):
if args:
raise TypeError('must use keyword argument!')
people_obj = self.__new__(self)
# self.__init__(people_obj, *args, **kwargs)
for attr, value in kwargs.items():
# people_obj.__dict__[attr.upper()] = value
setattr(people_obj, attr.upper(), value)
return people_obj
class People(metaclass=MyMeta):
pass
# MyMeta.__call__(People, name='egon', age=18):
obj = People(name='egon', age=18)
print(obj.__dict__) # {'NAME': 'egon', 'AGE': 18}
三. 在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性
# 解题思路: 在自定义元类中使用__call__.
class MyMeta(type):
# self=Student args=(name值, age值) 或 kwargs={'name': 值, 'age': 值}
def __call__(self, *args, **kwargs):
student_obj = self.__new__(self)
self.__init__(student_obj, *args, **kwargs)
# 注意: student_obj对象独有属性中可能已经指定了隐藏属性, 我们需要判断
student_obj.__dict__ = {attr if attr.startswith(f'_{self.__name__}') else f'_{self.__name__}__{attr}': value for attr, value in student_obj.__dict__.items()}
return student_obj
class Student(metaclass=MyMeta):
def __init__(self, name, age):
self.name = name
self.age = age
self.__balance = 0
self._score = {}
# MyMeta.__call__(Student, 'egon', 18):
obj = Student('egon', 18)
print(obj.__dict__)
四. 基于元类实现单例模式
详见第七节: https://www.cnblogs.com/yang1333/articles/12708842.html