一、今日作业
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