python 元类以及练习

'''
# 知识储备exec()
# 参数1:字符串形式的命令
# 参数2:全局作用域(字典形式),如果不指定默认就使用globals()
# 参数3:局部作用域(字典形式),如果不指定默认就使用locals()
'''
# g = {
#     'x': 2,
#     'y': 4
# }
# l = {}
# exec('''
# global x, m
# x = 10
# m = 100
#
# z = 3
# ''', g, l)
# print(g)
# print(l)
'''一切皆对象,元类(产生类的类叫元类,默认用class定义的类,他们的元类是type)'''
# 一切皆对象,对象可以怎么用?
# 1.都可以被引用,x = obj
# 2.都可以当做函数的参数传入
# 3.都可以当作函数的返回值
# 4.都可以当做容器类型的元素,l = [func, time, obj, l]

# # 类也是对象,Foo = type(...)
# class Foo:
#     pass
# obj = Foo()
# print(type(obj))
# print(type(Foo))
# #产生类的类叫元类,默认用class定义的类,他们的元类是type
'''定义类'''
# 方式一class
# class Chinese:
#     country = 'china'
#     def __init__(self, name):
#         self.name = name
#     def talk(self):
#         print('%s is talking' % self.name)
# print(Chinese)
# 方式二type
#定义类三要素 类名,类的基类, 类的名称空间
# class_name = 'Chinese'
# class_bases = (object,)
# class_body = '''
# country = 'china'
# def __init__(self, name):
#     self.name = name
# def talk(self):
#     print('%s is talking' % self.name)
# '''
# class_dic = {}
# exec(class_body, globals(), class_dic)
# # print(class_dic)
#
# Chinese1 = type(class_name, class_bases, class_dic)
# print(Chinese1)
'''自定义元类控制类的创建'''
# class Mymeta(type):
#     def __init__(self, class_name, class_bases, class_dic):
#         if not class_name.istitle():
#             raise TypeError('类名首字母必须大写')
#         if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
#             raise TypeError('必须有注释,且注释不能为空')
#         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
# class Chinese(object, metaclass = Mymeta):
#     '''
#     国人
#     '''
#     country = 'china'
#     def __init__(self, name):
#         self.name = name
#     def talk(self):
#         print('%s is talking' % self.name)
# c = Chinese('name')
# # Chinese = type(class_name, class_bases, class_dic)
'''知识储备__call__方法'''
# class Foo:
#     def __call__(self, *args, **kwargs):
#         print(self)
#         print(args)
#         print(kwargs)
# obj = Foo()
# obj(1, 2, 3, a=1, b=2, c=3)

# 元类内部也应该有一个__call__方法,会在调用Foo时触发执行
'''自定义元类控制类的实例化行为'''
# class Mymeta(type):
#     def __init__(self, class_name, class_bases, class_dic):
#         if not class_name.istitle():
#             raise TypeError('类名首字母必须大写')
#         if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
#             raise TypeError('必须有注释,且注释不能为空')
#         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
#     def __call__(self, *args, **kwargs):
#         # print(self)
#         # print(args)
#         # print(kwargs)
#         # 第一件事,造一个空对象
#         obj = object.__new__(self)
#         # 第二件事,初始化
#         self.__init__(obj, *args, **kwargs)
#         # 第三件事,返回obj
#         return obj
# class Chinese(object, metaclass = Mymeta):
#     '''
#     国人
#     '''
#     country = 'china'
#     def __init__(self, name):
#         self.name = name
#     def talk(self):
#         print('%s is talking' % self.name)
# obj = Chinese('xander')  # Chinese.__call__(Chinese, 'xander')
# print(obj.__dict__)
'''自定义元类控制类的实例化行为的应用'''
# 单例模式

# 实现方式一:
# class MySQL:
#     __instance = None
#
#     def __init__(self):
#         self.host = '127.0.0.1'
#         self.port = 3306
#     @classmethod
#     def singleton(cls):
#         if not cls.__instance:
#             obj = cls()
#             cls.__instance = obj
#         return cls.__instance
# # obj1 = MySQL()
# # obj2 = MySQL()
# # print(obj1)
# # print(obj2)
# obj1 = MySQL.singleton()
# obj2 = MySQL.singleton()
# print(obj1 is obj2)

# 实现方式二:元类的方式
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise TypeError('必须有注释,且注释不能为空')
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if not self.__instance:
            obj = object.__new__(self)
            self.__init__(obj)
            self.__instance = obj
        return self.__instance
class Mysql(object, metaclass=Mymeta):
    '''单例模式'''
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3306
    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj = cls()
            cls.__instance = obj
        return cls.__instance
obj1 = Mysql()
obj2 = Mysql()
print(obj1 is obj2)

练习

# 练习一:在元类中控制把自定义类的数据属性都变成大写
class Mymate(type):
    def __new__(cls, name, args, kwargs):
        obj = {}
        print(name)
        print(args)
        print(kwargs)
        for a, b in kwargs.items():
            if not a.startswith('__'):
                obj[a.upper()] = b
            else:
                obj[a] = b
        return type.__new__(cls, name, args, obj)
class Chinese(object, metaclass=Mymate):
    country = 'chinese'
    def talk(self):
        print('is talking')
print(Chinese.__dict__)

# 练习二:在元类中控制自定义的类无需init方法
# 1.元类帮其完成创建对象,以及初始化操作;
# 2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
# 3.key作为用户自定义类产生对象的属性,且所有属性变成大写
class Mymate(type):
    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError(':must use keyword argument')
        obj = object.__new__(self)  #创建对象,self为类Chinese

        for k, v in kwargs.items():
            obj.__dict__[k.upper()] = v
        return obj

class Chinese(object, metaclass=Mymate):
    country = 'chinese'
    def talk(self):
        print('is talking')
p = Chinese(name='egon', age=18, sex='male')
print(p.__dict__)