面向对象高级C(元类补充及单例模式)

元类有关知识点补充


#类的名称空间

类的名称空间不能用类似字典的方法修改,例如School类里面有name属性xxx,那么我用School.__dict__[‘name’] = ‘yyy’就会报错“TypeError: 'mappingproxy' object does not support item assignment”原因是类的名称空间和对象的名称空间不一样,后者是一个`a plain dict`,就是普通的字典,而后者是一个`mappingproxy`,不可映射类型,是不含__setattr__方法的 

#class底层原理的实现
Person = type('Person', (object,), {})

def __init__(self, name, age):
    self.name = name
    self.age = age
p = type('Person', (object, ), {'x': 1, '__init__': __init__})
p1 = p('ljy', 19)

class Person:
    x = 1
    def __init__(self, name, age):
        self.name = name
        self.age = age
p = Person
q = p('ljy', 18)
print(p.__dict__)
print(q.__dict__)


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

ss = '''
global x  #全局变量要加在最前面
x = 1
y = 2
z = 3


print(y)

'''
g = {}
l = dict()
exec(
    ss, g, l
)

print(print(1))  #会打印出1,和None,因为print的返回值是None

print(l) # 先把y=2打印出来,然后再打印局部变量的字典
print(g) #打印全部变量的字典,里面包含x:2,在字典的末尾

# 自定义元类,继承type

class Mymeta(type):
    def __init__(self, name, bases, dic):
        #self 是Person这个类(对象)
        #在这个位置,其实self也就是Person这个类,
        #内部已经有东西了,名称空间里面的东西在__new__
        #可以通过过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,), {...})
Mymete类实例化,会把三个参数传到Mymeta的__init__方法中
'''

class PersonException(metaclass=Mymeta):
        name = 'lae'#加上这句话,类的名称空间里面就有name属性了
    def __init__(self,name,age):
        self.name = name
        self.age = age
#通过元类控制类的调用过程,实例化产生对象的过程
p1 = PersonException(18)
#因为类中没有name属性,所以会抛异常‘没有name属性,不能创建’

class Person():
    def __init__(self, name):
        self.name = name
        raise Exception('就不让你创建')

p = Person('nick')
# 自动触发Person类__init__的执行,
# 抛异常:就不让你创建
# 总结:可以通过自定义元类,重写__init__方法来控制类的产生


class Mymeta(type):
    def __init__(self, name, bases, dic):
        if 'name' not in dic.keys():
            raise Exception('没有name属性,不能创建')
        else:
            self.__dict__['name'] = 'nick'
            '''
            会报错TypeError: 'mappingproxy'
            object does not support item assignment
            也就是类的名称空间是一个字典,但是不能对里面的值进行修改
            (错误详情见下一篇博客整理)
            '''

class Person(metaclass=Mymeta):
    name = 'ljy'
    def __init__(self, name, age):
        self.name = name
        self.age = age
p = Person('ljy', 19)
print(p.name)
print(Person.name)


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,说明
        # self.__new__就是object.__new__

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

单例模式

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

PORT = '192.384.28.2'
HOST = 2728
#方式一:通过类的绑定方法
#当用户不输入端口和地址,每次拿到的对象都是同一个。
class Sql:
    _instance = None
	def __init__(self, port, host):
        self.port = port
        self.host = host
	@classmethod
    def singleton(cls):
        if not cls._instance:
            cls._instance = cls(PORT, HOST)
        return cls._instance
p1 = Sql.singleton()
p2 = Sql.singleton()
p3 = Sql('33306','192.168.1.1')#当用户输入端口和地址,实例化产生新对象
print(p1)
print(P2)
print(p3)
'''
<__main__.Sql object at 0x00000225BDBABFD0>
<__main__.Sql object at 0x00000225BDBABFD0>
<__main__.Sql object at 0x00000225BDBABF98>
'''

##方式二:通过装饰器
#不传参,实例化对象内存地址相同
def get_singleton(cls):
    _instance = None
    def inner(*args, **kwargs):
        if len(args)!=0 or len(kwargs)!=0:
            res = cls(*args, **kwargs)
            return res
        else:
            nonlocal _instance
            if not _instance:
                _instance = cls(PORT, HOST)
            return _instance
    return inner

@get_singleton
class Sql:
    def __init__(self, port, host):
        self.port = port
        self.host = host
s1=Sql()
s2=Sql()
s3=Sql('33306','192.168.1.1') #传参,实例化对象地址不同
print(s1)
print(s2)
print(s3)
'''
<__main__.Sql object at 0x00000185E9601978>
<__main__.Sql object at 0x00000185E9601978>
<__main__.Sql object at 0x00000185F04CE2B0>
'''

#方法三:用自定义元类
#不传参,实例化对象内存地址相同
class Mymeta(type):
	def __init__(cls, name, bases, dic):
        cls._instance = cls(PORT, HOST)
    def __call__(cls, *args, **kwargs):
        if len(args)!=0 or len(kwargs)!=0:
            obj = object.__new__(cls)
            obj.__init__(*args, **kwargs)
            return obj
        else:
            return cls._instance
class Sql(metaclass=Mymeta):
    def __init__(self, port, host):
        self.port = port
        self.host = host
p1 = Sql()
p2 = Sql()
p3 = Sql(8001, '178.23.4.53')#传参,实例化对象地址不同
print(p1)
print(p2)
print(p3)
"""
<__main__.Sql object at 0x000002469196E320>
<__main__.Sql object at 0x000002469196E320>
<__main__.Sql object at 0x000002469196E438>

"""

#方法四:导入模块
#singleton.py
class Sql():
    def __init__(self, port, host):
        self.port = port
		self.host = host
        
s1 = Sql(PORT, HOST)
        
#test.py
def test1():
    from singleton import s1
    print(s1)
def test2():
    from singleton import s1
    print(s1)

test1()
test2()
from singleton import s1
from singleton import Sql
s2 = Sql(477, '182.22.2.2')
print(s1)
print(s2)

'''
<sigonleton.Sql object at 0x000001B0456F53C8>
<sigonleton.Sql object at 0x000001B0456F53C8>
<sigonleton.Sql object at 0x000001B0456F53C8>
<sigonleton.Sql object at 0x000001B045B3F080>

'''
    
    
posted @ 2019-09-05 19:59  Micheal_L  阅读(201)  评论(0编辑  收藏  举报