元类-练习

一. 在元类中控制把自定义类的数据属性都变成大写(提示: 内置数据属性不修改)

"""
思路: 明白包含类的名称空间在自定义元类中只有__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

posted @ 2020-04-16 00:53  给你加马桶唱疏通  阅读(263)  评论(0编辑  收藏  举报