python基础学习-day33==课后作业练习
一、今日作业
1.在元类中控制把自定义类的数据属性都变成大写
cls_singleton.py模块
class Foo(object): pass instance = Foo()
class Mymetaclass(type): # Chinese, Chinese.__name__, (), {country:China, tag:..., walk函数名称:...} def __new__(cls, name, bases, attrs): # print(cls) update_attrs = {} for k, v in attrs.items(): # callable(方法) 判断是否是可调用的函数或方法 # 不是 方法 与 私有属性, 都将属性名改为大写 if not callable(v) and not k.startswith('__'): update_attrs[k.upper()] = v else: update_attrs[k] = v return type.__new__(cls, name, bases, update_attrs) class Chinese(metaclass=Mymetaclass): # Mymetaclass() country = 'China' tag = 'Legend of the Dragon' # 龙的传人 def walk(self): print('%s is walking' % self.name)
# print('第一题:', Chinese.__dict__)
2.在元类中控制自定义的类无需__init__方法
1.元类帮其完成创建对象,以及初始化操作; 2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument 3.key作为用户自定义类产生对象的属性,且所有属性变成大写 class Mymetaclass(type): # 调用Chinese类时会触发该方法 self ---> Chinese def __call__(self, *args, **kwargs): # 判断如果传递进来的是位置参数,则抛出异常 if args: raise TypeError('must use keyword argument for key function') # 创造一个空的对象 obj = object.__new__(self) # 创建对象,self为类Foo # **name='lili', age=18, sex='male' ---> {name:'lili', age:18, sex:'male'} for k, v in kwargs.items(): # k ---> name , v ---> lili # 修改对象的名称空间中的属性 obj.__dict__[k.upper()] = v # 将修改后的对象返回 return obj class Chinese(metaclass=Mymetaclass): # obj = Mymetaclass() ---> Chinese = obj country = 'China' tag = 'Legend of the Dragon' # 龙的传人 def walk(self): print('%s is walking' % self.name) # 默认继承object,内部会先自动触发 __new__ --> obj ---> 接着去触发__init__(obj) p = Chinese(name='lili', age=18, sex='male') print(p.__dict__)
3.在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性
class Mymeta(type): # 当自定义类,指向自定义元类时,会自动调用自定义元类中的__init__, 当前自定义元类没有,则找type类中的 def __init__(self, class_name, class_bases, class_dic): # 控制类Foo的创建 super(Mymeta, self).__init__(class_name, class_bases, class_dic) # super(Mymeta, self) - --> type # type.__init__(self, class_name, class_bases, class_dic) # 控制: 定义类时的创建过程 # def __new__(cls, *args, **kwargs): # pass # 控制调用类创建对象初始化的过程 def __call__(self, *args, **kwargs): # args ---> 'lili', 18, 'male' # 1、控制Foo的调用过程,即Foo对象的产生过程 obj = self.__new__(self) # 先调用Foo类中__init__方法,将空的对象与需要接收的参数一并传给类中的__init__ # __init__(obj, 'lili', 18, 'male') self.__init__(obj, *args, **kwargs) # 已经初始化赋值完毕 print(obj.__dict__) # 字典生成式 # for k, v in obj.__dict__.items(): 遍历对象的名称空间 {'name': 'tank', ..} # '_%s__%s' % (self.__name__, k): 将公开的数据名 改为 为 _类名__属性名 obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in obj.__dict__.items()} return obj class Foo(object, metaclass=Mymeta): # Foo = Mymeta(...) __a = None # -> _Foo__a def __init__(self, name, age, sex): # self ---> obj.name = name self.name = name self.age = age self.sex = sex # @property # def get_name(self): # return self.__name obj = Foo('lili', 18, 'male') # print('问题3:', obj.__dict__) # print(obj.name) # 错误 # print(obj._Foo__name)
4.基于元类实现单例模式
#5种单列模式
''' 单例模式: 单例模式是一个软件的设计模式,为了保证一个类,无论调用多少次产生的实例对象, 都是指向同一个内存地址,仅仅只有一个实例(对象)! 五种单例: - 模块 - 装饰器 - 元类 - __new__ - 类方法: classmethod ''' class People: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 调用三次相同的类,传递相同的参数,产生不同的三个实例 p1 = People('tank', 17, 'male') p2 = People('tank', 17, 'male') p3 = People('tank', 17, 'male') # print(p1 is p2 is p3) # 打开同一个文件的时候,链接MySQL数据库 ''' 伪代码 mysql_obj1 = MySQL(ip, port) mysql_obj2 = MySQL(ip, port) mysql_obj3 = MySQL(ip, port) ''' ''' 方式一: @classmethod ---> 通过类方法来实现单例 ''' class Foo(object): # 定义了一个类的数据属性, # 用于接收对象的实例,判断对象的实例是否只有一个 _instance = None # obj1 def __init__(self, name, age): self.name = name self.age = age @classmethod def singleton(cls, *args, **kwargs): # 判断类属性_instance是否有值,有代表已经有实例对象 # 没有则代表没有实例对象,则调用object的__init__获取实例对象 if not cls._instance: # object.__new__(cls): 创造对象 # 没有参数情况下 # cls._instance = object.__new__(cls, *args, **kwargs) # 有参数的情况下 cls._instance = cls(*args, **kwargs) # Foo() # 将已经产生的实例对象 直接返回 return cls._instance obj1 = Foo.singleton('tank', '123') obj2 = Foo.singleton('tank', '123') # print(obj1 is obj2) ''' 方式二: 元类 ''' class MyMeta(type): # 1、先触发元类里面的__init__ def __init__(self, name, base, attrs): # self --> Goo # *** 造空的对象, 然后赋值给了Goo类中的_instance类属性 self._instance = object.__new__(self) # 将类名、基类、类的名称空间,传给type里面的__init__ super().__init__(name, base, attrs) # type.__init__(self, name, base, attrs) # 2、当调用Goo类时,等同于调用了由元类实例化的到的对象 def __call__(self, *args, **kwargs): # 判断调用Goo时是否传参 if args or kwargs: init_args = args init_kwargs = kwargs # 1)通过判断限制了用于传入的参数必须一致,然后返回同一个对象实例 if init_args == args and init_kwargs == kwargs: return self._instance # 2) 若不是同一个实例,则新建一个对象,产生新的内存地址 obj = object.__new__(self) self.__init__(obj, *args, **kwargs) return obj return self._instance class Goo(metaclass=MyMeta): # Goo = MyMeta(Goo) # _instance = obj def __init__(self, x): self.x = x g1 = Goo('1') g2 = Goo('1') # print(g1 is g2) # True ''' 方式三: __new__实现 ---> 通过调用类方法实例化对象时,自动触发的__new__来实现单例 ''' class Aoo(object): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = object.__new__(cls) return cls._instance a1 = Aoo() a2 = Aoo() # print(a1 is a2) # True ''' 方式四: 装饰器实现 ---> 通过调用类方法实例化对象时,自动触发的__new__来实现单例 ''' # 单例装饰器 def singleton_wrapper(cls): # cls ---> Too # 因为装饰器可以给多个类使用,所以这里采用字典 # 以类作为key, 实例对象作为value值 _instance = { # 伪代码: 'Too': Too的示例对象 } def inner(*args, **kwargs): # 若当前装饰的类不在字典中,则实例化新类 # 判断当前装饰的Too类是否在字典中 if cls not in _instance: # obj = cls(*args, **kwargs) # return obj # 不在,则给字典添加 key为Too, value为Too()---> 实例对象 # {Too: Too(*args, **kwargs)} _instance[cls] = cls(*args, **kwargs) # return 对应的实例对象cls(*args, **kwargs) return _instance[cls] return inner @singleton_wrapper # singleton_wrapper(Too) class Too(object): pass t1 = Too() t2 = Too() # print(t1 is t2) # True ''' 方式五: 模块导入实现 ''' import cls_singleton s1 = cls_singleton.instance s2 = cls_singleton.instance print(s1 is s2) # True