day37-2元类,单例模式

元类

  • type
  • 一个造类的对象
  • 实例化:元类-->类-->对象
class Foo:
    pass

f = Foo()
print(f.__class__)
print(f.__class__.__class__)
<class '__main__.Foo'>
<class 'type'>

造类的第一种形式

  • 使用type模仿class关键字造类

class做了什么事

类的三部分

  1. 类名:class_name = Foo
  2. 类体代码:class_body开辟内存空间,把属性和方法放入一个名称空间里
  3. 父类(基类):class_bases = (object,)
# eval方法:用来解析某一个具体的数据类型
# eval('[1, 1]')    # [1, 1]    

# evec方法:会把字符里的代码运行,并且放入名称空间
class_body = '''
count = 1
'''

class_name = 'Foo'
class_bases = (object,)     # 加了逗号才是元祖

class_dic = dict()
exec(class_body, {}, class_dic)     # {}里是当前所有的内置方法
Foo1 = type(class_name, class_bases, class_dic)
print(Foo1)     # Foo1只是变量名,实际上的类名是由class_name定的
print(Foo1.count)
<class '__main__.Foo'>
1

控制元类产生的类

  • 在type和类的中间加一层控制

  • Foo= Mymeta(class_name, class_bases, class_dic)

  • 用super是为了调用type类里的

class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic): # 只是形参,叫什么都可以
        # 这里可以对类的属性进行操作,如:
        if class_name.islower():
            raise TypeError('类名必须大写')
            
        if not class_dic.get('__doc__'):    # ''''''的注释在字典中保存键名为__doc__
            raise TypeErroe('必须得加注释')
            
        # 方法继承自type
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        
class Foo(metaclass=Mymeta):    # 把类名,类体代码,父类传递给Mymeta,进而产生类
    '''这种注释才有doc'''
    def __init__(self, name):
        self.name = name
        
        
print(Foo)
print(Foo.__class__)
print(Foo.__dict__)
<class '__main__.Foo'>
<class '__main__.Mymeta'>   # 实例化出他的类已变成了Mymeta
{'__module__': '__main__', '__doc__': '这种注释才有doc', '__init__': <function Foo.__init__ at 0x0000015E63D7D950>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>}

控制元类产生的对象

  • 实例化类的时候会自动触发__init__方法,(实际上是触发__new__
  • 实例化对象的时候会自动触发__call__

  • 举例验证:

class Foo():
    def __init__(self):
        print('init')
    def __call__(self, *args, **kwargs):
        print('call')

f = Foo()
f()
init
call
  • 控制对象产生
class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        
    def __call__(self, *args, **kwargs):
        # self就是People, args就是name和x,kwargs没传值
        print('self', self)
        print(args, kwargs)
        
        # 以下是造对象的过程
        # 造一个空类
        obj = self.__new__(self)    # 造一个空类{},固定写法
        print(1, obj.__dict__)
        
        # 造对象
        # 相当于调用People.__init__({}, 'leijun', 1)
        self.__init__(obj, *args, **kwargs)
        
        # obj就变成了{'name', 'leijun'}
        print(2, obj.__dict__)
        return obj  # 对象实例化
    
class People(object, metaclass=Mymeta):
    def __init__(self, name, x):
        self.name = name
 
P = People('leijun', 1)
self <class '__main__.People'>
('leijun', 1) {}
1 {}
2 {'name': 'leijun'}

实例化类

  • 实际上是在调用__new__
class Foo:
    def __new__(cls, *args, **kwargs): # 传类
        print(cls)
        print('new')
        return object.__new__(cls)      # 这里要用父类,不能用cls,不然会一直循环
    
    def __init__(self):
        print('init')
       
f = Foo()
print(f)

加上元类后类的属性查找顺序

  • 对象本身-->类-->父类-->父类-->object-->type找不到报错

元类控制模版

class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        # 这里控制类的产生
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        
    def __call__(self, *args, **kwargs):
        # 这里控制实例化对象时传的参数
        obj = self.__new__(self)    # 建空类
        self.__init__(obj, *args, **kwargs) #加属性
        
        # 这里控制对象的产品
        return obj

单例模式

  • 类用同一种方式调用多个东西时,产生的都是同一个东西

1. 使用类方法的特性

NAME = 'leijun'

class People():
    __instance = None   # 默认值
    
    def __init__(self, name):
        self.name = name
    
    @classmethod
    def from_conf(cls):
        if cls.__instance:  # 如果默认值已产生就按默认值,避免二次产生导致id不同
            return cls.__instance
        
        cls.__instance = cls(NAME)  # 没产生就产生默认值
        return cls.__instance
    
p1 = People.from_conf()
p2 = People.from_conf()
print(id(p1))   # 2242450203144
print(id(p2))   # 2242450203144

2. 使用装饰器

NAME = 'leijun'

def deco(cls):
    cls.__instance = cls(NAME)  # 默认值
    
    def wrapper(*args, **kwargs):
        if len(args) == 0 and len(kwargs) == 0:     # 没输出就按默认值
            return cls.__instance
        
        res = cls(*args, **kwargs)
        return res
    
    return wrapper

@deco
class People():
def __init__(self, name):
    self.name = name 
    
p1 = People()
p2 = People()
print(id(p1))   # 2458763959432
print(id(p2))   # 2458763959432

3. 使用元类

NAME = 'leijun'

class Mymeta(type):
    
    def __init__(self, class_name, class_bases, class_dic):
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        self.__instance = self(NAME)
        
    def __call__(self, *args, **kwargs):
        if len(args) == 0 and len(kwargs) == 0:
            return self.__instance
        
        obj = self.__new__(self)    # 这是可以用self因为这个self本身没有new方法,所以也要去找父类,就不会出现循环,如果self本身有new,就用父类名,最好是用object
        self.__init__(obj, *args, **kwargs)
        return obj
    
class People(metaclass=Mymeta):
    def __init__(self, name):
        self.name = name
        
p1 = People()
p2 = People()
print(id(p1))
print(id(p2))

转载于:https://www.cnblogs.com/lucky75/p/11066829.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值