面向对象——元类,单例模式,属性查找

一、 元类(***)难点

定义类,控制对象产生

定义元类,控制类产生(看源码时用)

一切源自于一句话:python中一切皆为对象
1、元类介绍
 对象是调用类得到
元类是调用元类得到
查看类(就是类型):type(obj)===>OldboyTeacher
查看元类(就是类型):type(OldboyTeacher)==>type
# 元类=》OldboyTeacher类=》obj
class OldboyTeacher(object):
    school = 'oldboy'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' % self.name)


obj = OldboyTeacher('egon', 18)  # 调用OldboyTeacher类=》对象obj
#                                  调用元类=》OldboyTeacher类

# print(type(obj))
print(type(OldboyTeacher))
# 结论:默认的元类是type,默认情况下我们用class关键字定义的类都是由type产生的

2、class关键字底层的做了哪些事造出来类? 

一定是调用了type得到返回值,把返回值赋值给class关键字

# 1、先拿到一个类名
class_name = "OldboyTeacher"

# 2、然后拿到类的父类
class_bases = (object,)

# 3、再运行类体代码,将产生的名字放到名称空间中
class_dic = {}
class_body = """
school = 'oldboy'

def __init__(self, name, age):
    self.name = name
    self.age = age

def say(self):
    print('%s says welcome to the oldboy to learn Python' % self.name)
"""
exec(class_body,{},class_dic)
# print(class_dic)

# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = type(class_name,class_bases,class_dic)

3、自定义元类

只有继承了type类的类才是自定义的元类
type是内置的元类,type内部一定有个__init__用法
class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
    pass


class OldboyTeacher(object, metaclass=Mymeta):
    school = 'oldboy'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' % self.name)


# 1、先拿到一个类名:"OldboyTeacher"
# 2、然后拿到类的父类:(object,)
# 3、再运行类体代码,将产生的名字放到名称空间中{...}
# 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})

 4、自定义元类来控制OldboyTeacher类的产生

import re

class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
    def __init__(self, class_name, class_bases, class_dic):
        # print(self)  # 类<class '__main__.OldboyTeacher'>
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        if not re.match("[A-Z]", class_name):
            raise BaseException("类名必须用驼峰体")

        if len(class_bases) == 0:
            raise BaseException("至少继承一个父类")

        # print("文档注释:",class_dic.get('__doc__'))
        doc=class_dic.get('__doc__')

        if not (doc and len(doc.strip()) > 0):
            raise BaseException("必须要有文件注释,并且注释内容不为空")

# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object,metaclass=Mymeta):
    """
    adsaf
    """

    school = 'oldboy'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' % self.name)

5、自定义元类来控制OldboyTeacher类的调用

自定义元类的目的是:定义规范

这里涉及到如何隐藏属性问题:其实就是利用字典的k:v(此例子中有)

import re

class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
    def __init__(self, class_name, class_bases, class_dic):
        # print(self)  # 类<class '__main__.OldboyTeacher'>
        # print(class_name)
        # print(class_bases)
        # print(class_dic)

        if not re.match("[A-Z]", class_name):
            raise BaseException("类名必须用驼峰体")

        if len(class_bases) == 0:
            raise BaseException("至少继承一个父类")

        # print("文档注释:",class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')

        if not (doc and len(doc.strip()) > 0):
            raise BaseException("必须要有文件注释,并且注释内容不为空")

    # res = OldboyTeacher('egon',18)
    def __call__(self, *args, **kwargs):
        # 1、先创建一个老师的空对象
        tea_obj = object.__new__(self)
        # 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__
        self.__init__(tea_obj, *args, **kwargs)
        tea_obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}

        # 3、将初始化好的老师对象赋值给变量名res
        return tea_obj


# OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
class OldboyTeacher(object, metaclass=Mymeta):
    """
    adsaf
    """

    school = 'oldboy'

    #            tea_obj,'egon',18
    def __init__(self, name, age):
        self.name = name  # tea_obj.name='egon'
        self.age = age  # tea_obj.age=18

    def say(self):
        print('%s says welcome to the oldboy to learn Python' % self.name)


res = OldboyTeacher('egon', 18)
print(res.__dict__)
# print(res.name)
# print(res.age)
# print(res.say)

# 调用OldboyTeacher类做的事情:
# 1、先创建一个老师的空对象
# 2、调用老师类内的__init__方法,然后将老师的空对象连同括号内的参数的参数一同传给__init__
# 3、将初始化好的老师对象赋值给变量名res

6、仅供理解的例子

class Foo:
    def __call__(self, *args, **kwargs):
        print('================>')
        print(self)
        print(args)
        print(kwargs)


obj1 = Foo()
obj1(1,2,3,a=1,b=2)  # 调用对象其实就是在调用对象类中定义的绑定方法__call__
#
# obj2 = int(10)
# obj2()

# obj3 = list([1, 2, 3])
# obj3()


二、 单例模式

字面意思:保证一个类仅有一个实例,并只提供一个访问它的全局访问点

目的:节省内存空间

设计模式的一种

#优点:
1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
3、单例可长驻内存,减少系统开销。
#缺点:
1、单例模式的扩展是比较困难的;
2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到);
3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试;
4、单例模式在某种情况下会导致“资源瓶颈”。

#应用举例:
1、生成全局惟一的序列号;
2、访问全局复用的惟一资源,如磁盘、总线等;
3、单个对象占用的资源过多,如数据库等;
4、系统全局统一管理,如Windows下的Task Manager;
5、网站计数器。

 更多模式:https://www.cnblogs.com/liuqingzheng/p/10038958.html

三种方式实现单例模式

IP='192.1.1.1'
PORT=3306
settings
1、实现方式1:classmethod
import settings

class MySQL:
    __instance = None

    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    @classmethod
    def singleton(cls):
        if cls.__instance:
            return cls.__instance
        cls.__instance = cls(settings.IP, settings.PORT)
        return cls.__instance


# obj1=MySQL("1.1.1.1",3306)
# obj2=MySQL("1.1.1.2",3306)
# print(obj1)
# print(obj2)


obj3 = MySQL.singleton()
print(obj3)

obj4 = MySQL.singleton()
print(obj4)

2、实现方式2:元类

import settings

class Mymeta(type):
    __instance = None
    def __init__(self,class_name,class_bases,class_dic):
        self.__instance=object.__new__(self)  # Mysql类的对象
        self.__init__(self.__instance,settings.IP,settings.PORT)

    def __call__(self, *args, **kwargs):
        if args or kwargs:
            obj = object.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
        else:
            return self.__instance

# MySQL=Mymeta(...)
class MySQL(metaclass=Mymeta):
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port


# obj1 = MySQL("1.1.1.1", 3306)
# obj2 = MySQL("1.1.1.2", 3306)
# print(obj1)
# print(obj2)

obj3 = MySQL()
obj4 = MySQL()

print(obj3 is obj4)

3、实现方式3:装饰器

import settings

def outter(func):  # func = MySQl类的内存地址
    _instance = func(settings.IP,settings.PORT)
    def wrapper(*args,**kwargs):
        if args or kwargs:
            res=func(*args,**kwargs)
            return res
        else:
            return _instance
    return wrapper

@outter  # MySQL=outter(MySQl类的内存地址)  # MySQL=》wrapper
class MySQL:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port


# obj1 = MySQL("1.1.1.1", 3306)
# obj2 = MySQL("1.1.1.2", 3306)
# print(obj1)
# print(obj2)

obj3 = MySQL()
obj4 = MySQL()
print(obj3 is obj4)

三、属性查找

按照之前的查找顺序

在有元类的情况下的属性查找顺序:一直找到父类找到object后再找元类

 

 

class Mymeta(type):
    n=444

    # def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
    #     obj=self.__new__(self)
    #     print(self.__new__ is object.__new__) #True


class Bar(object):
    # n=333

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')
    pass

class Foo(Bar):
    # n=222

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')
    pass

class OldboyTeacher(Foo,metaclass=Mymeta):
    # n=111

    school='oldboy'

    def __init__(self,name,age):
        # self.n=0
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)


    # def __new__(cls, *args, **kwargs):
    #     print('OldboyTeacher.__new__')


# obj=OldboyTeacher('egon',18)
# print(obj.n)

print(OldboyTeacher.n)

 

---34---
posted @ 2020-08-18 09:08  1024bits  阅读(183)  评论(0编辑  收藏  举报