day 30 元类更深认识,单例

元类更深认识

#type只传一个参数有什么用?
打印对象的类型

#class 底层原理,通过type类来实例化得到类
def __init__(self,name):
self.name=name
Person=type('Person',(object,),{'x':1,'__init__':__init__})
p=Person('lqz')
print(Person.__dict__)
print(p.__dict__)
print(p.x)
等同于
class Person(object):
x=1
def __init__(self, name):
self.name = name
p=Person('lqz')
print(Person.__dict__)
print(p.__dict__)
print(p.x)


#exec 执行字符串的代码,当成python解释器

ss='''
x=1
print(x)
'''
g={}
l={}
exec(ss,g,l)
print(l)


#自定义元类:继承type
class Mymeta(type):
def __init__(self,name,bases,dic):
#self 是Person 这个类(对象)
#在这个位置,其实self也就说Person这个类,内部已经有东西了,名称空间已经有东西了 就是dic
#所以在这个地方,可以通过dic来判断名称空间
#也可以直接通过self.__dict__/self.属性 来判断
a=dic.get('name')
if not a :
raise Exception('没有name属性,不能创建')
# def __call__(self, *args, **kwargs):
# pass

class Person(metaclass=Mymeta): #等同于Person=Mymeta('Person',(object,),{...}) Mymeta类实例化,会把三个参数传到Mymeta的__init__方法中
def __init__(self,name):
self.name=name
raise Exception('就不让你创建')


p=Person('lqz') #自动触发Person类__init__的执行
总结:可以通过自定义元类,重写__init__方法来控制类的产生


#通过元类控制类的调用过程,实例化产生对象的过程

class Mymeta(type):
def __call__(self, *args, **kwargs):
#该方法必须返回一个对象(类对象),这个地方返回什么 p=Person('lqz') p就是什么
#返回一个真正的Person类的对象
#第一步:产生一个空对象
object.__new__(self) 传一个参数,传类,就会产生一个该类的空对象
#obj是Person类的空对象
obj=object.__new__(self)
print(self.__new__ is object.__new__) # True

#第二步:初始化该对象,把初始值放到对象中
obj是Person类的空对象 obj.__init__ 调用自己的绑定方法,也就说Person类中写的__init__方法
obj.__init__(*args, **kwargs)
#还可以类来调用
Person.__init__(obj,*args, **kwargs)
self.__init__(obj,*args, **kwargs)
print(obj.name)
#第三步:把该对象返回
return obj

class Person(metaclass=Mymeta):
# class Person():
def __init__(self,name):
self.name=name
def __call__(self, *args, **kwargs):
print('xxx')


p=Person('lqz')
print(p.name)
print(p)
# 原来的理解Person('lqz') 会调用Person类的__init__的方法
# 这个位置会调用元类的__call__方法,所以在__call__方法中调用了Person __init__方法,来完成对象的初始化
p()会调类中的__call__方法


模板:控制对象的产生
class Mymeta(type):
def __call__(self, *args, **kwargs):
obj=object.__new__(self)
obj.__init__(*args, **kwargs)
return obj

class Person(metaclass=Mymeta):
def __init__(self,name):
self.name=name
def __call__(self, *args, **kwargs):
print('xxx')

p=Person('lqz')




加深部分
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)

object.__new__ 传哪个类就得到哪个类的空对象
p=object.__new__(Person)
print(p)

__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(name)
# print(bases)
# print(dic)
# 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='oldboy'
age=10
def __init__(self,name,age):
self.name=name
self.age=age


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

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

实现单利的第一种方法(通过类的绑定方法)
当用户输入端口和地址,实例化产生新对象
当用户不输入端口和地址,每次拿到的对象,都是同一个
class Sql():
_instance=None
def __init__(self,port,host):
self.port=port
self.host=host
@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)
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的模块是天然的单例)

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)
原文地址:https://www.cnblogs.com/wwei4332/p/11460427.html