元类补充以及单例模式

元类补充

object._new_

class Person():
    def __init__(self,name,age):
        print('__init__')
        self.name=name
        self.age=age
    def __new__(cls, *args, **kwargs):
        print('__new__')
        #生成一个Person类的空对象
        return object.__new__(cls)
 
p=Person('lqz',19)
print(p)

会发现结果为:

_new_
__init__
<__main__.Person object at 0x000002217E7099E8>

为什么先会调用__new__呢?

因为Person加括号先调用了type类里的_call_,然后__call__调用了Person类的__new__

_new_ 和__init__

_new_ 创建空对象

_init_ 初始化空对象

object._new_(Person) :生成Person类的对象 空的

type._new_(cls,name,bases,dic) :生了cls这个类对象,里面有东西

元类中:

__init__:控制类的产生,在__new__之后

_call_:对着对象的产生

__new__:控制类产生最根上,其实本质最根上也不是它,是type的__call__,但是我们控制不了了

class Mymeta(type):
    def __init__(self,name,bases,dic):
        #self 是Person类,Person类中有名称空间之类的了
        # print('xxxxxxx')
        # print(args)
        # print(kwargs)
        # print(name)
        # print(bases)
        # print(dic)
        pass
        # self.name='xxxxxxx'
    def __new__(cls, name,bases,dic):
        # print(name)
        # print(bases)
        # print(dic)
        #产生空对象(空类),在这里面生成的并不是空类,是有数据的类了
        #如何完成类的初始化,并且把name,bases,dic这些东西放入
        # return type.__new__(cls,name,bases,dic)
        dic2={'attr':{}}
        for k,v in dic.items():
            #加入这一句,类名称空间中带__的就不会放到attr中
            if not k.startswith('__'):
                dic2['attr'][k]=v
        print('-------',dic2)
        return type.__new__(cls,name,bases,dic2)


class Person(metaclass=Mymeta):     # Person=Mymeta(name,bases,dic)   调用type的__call__,内部调用了Mymeta.__new__,又掉Mymeta的__init__
    school='音乃木板学院'
    age=10
    def __init__(self,name,age):
        self.name=name
        self.age=age


print(Person.__dict__)
print(Person.attr['school'])
# p=Person('nick',18)

单例模式

单例模式是设计模式中的一种,其中一共有23种设计模式

单例模式:整个过程中只有一个实例,所有生成的实例都指向一块内存空间

方法一:

可以通过类的绑定方法来实现:当用户输入端口和地址,实例化产生新对象,当用户不输入端口和地址,每次拿到的对象,都是同一个

# settings.py
PORT=3306
HOST='127.0.0.1'
class Sql():
    _instance=None
    def __init__(self,port,host):
        self.port=port
        self.host=host
    # @classmethod
    # def get_sigoleton(cls):
    #     import settings
    #     if cls._instance:
    #         return cls._instance
    #     else:
    #         cls._instance=cls(settings.PORT,settings.HOST)
    #     return cls._instance
    @classmethod
    def get_sigoleton(cls):
        import settings
        if not cls._instance:
            cls._instance = cls(settings.PORT, settings.HOST)
        return cls._instance

#每次调用get_sigoleton 拿到的对象都是同一个
s1=Sql.get_sigoleton()
s2=Sql.get_sigoleton()
s3=Sql.get_sigoleton()

print(s1)
print(s2)
print(s3)
s4=Sql('33306','192.168.1.1')
print(s4)

方法二:

通过装饰器

def get_sigoleton(cls):
    #cls就是Sql这个类
    import settings
    _instance=cls(settings.PORT, settings.HOST)
    # _instance=Sql(settings.PORT, settings.HOST)

    def wrapper(*args,**kwargs):
        if len(args)!=0 or len(kwargs)!=0:
            #表示传了参数,生成新对象
            res=cls(*args,**kwargs)
            return res
        else:
            return _instance
    return wrapper

# def get_sigoleton(cls):
#     _instance=None
#     def wrapper(*args,**kwargs):
#         if len(args)!=0 or len(kwargs)!=0:
#             #表示传了参数,生成新对象
#             res=cls(*args,**kwargs)
#             return res
#         else:
#             import settings
#             nonlocal _instance
#             if not _instance:
#                 _instance=cls(settings.PORT, settings.HOST)
#             return _instance
#     return wrapper

@get_sigoleton    #会把下面的Sql当中参数传入,相当于:Sql=get_sigoleton(Sql)
class Sql():
    def __init__(self,port,host):
        self.port=port
        self.host=host
# Sql=get_sigoleton(Sql)
s1=Sql()
s2=Sql()
s3=Sql('33306','192.168.1.1')
s4=Sql('33306','192.168.1.1')
print(s1)
print(s2)
print(s3)

方法三

通过元类

class Mymeta(type):
    def __init__(self,name,bases,dic):
        #self 是Sql类
        import settings
        #把实例化好的对象,放到了类的名称空间
        self._instance=self(settings.PORT, settings.HOST)
    def __call__(self, *args, **kwargs):
        #self是谁?是Sql类
        if len(args)!=0 or len(kwargs)!=0:
            obj=object.__new__(self)
            obj.__init__(*args, **kwargs)
            return obj
        else:
            return self._instance

class Sql(metaclass=Mymeta):    #相当于 Sql=Mymeta(name,bases,dic)   这个会调用 Mymeta的__init__  在里面已经向类的名称空间放了一个对象
    def __init__(self,port,host):
        self.port=port
        self.host=host

print(Sql.__dict__)
s1=Sql()
#调用元类的__call__
s2=Sql()
s3=Sql('33306','192.168.1.1')
print(s1)
print(s2)
print(s3)

方法四

利用模块导入(python的模块是天然的单例)

# sigonleton.py

import settings
class Sql():
    def __init__(self,port,host):
        self.port=port
        self.host=host

s1=Sql(settings.PORT,settings.HOST)
def test():
    from sigonleton import s1
    print(s1.port)
    print(s1)
def test2():
    from sigonleton import s1 as s2
    print(s2)

test()
test2()
from sigonleton import s1
from sigonleton import Sql
s2=Sql(3306,'192.168.1.1')
print(s1)
print(s2)
posted @ 2019-09-04 16:30  黑井白子  阅读(125)  评论(0编辑  收藏  举报
Live2D