面向对象之 —— 类、元类、单例

Python中类的概念借鉴于Smalltalk。在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立。

class ObjectCreator(object):
    pass

my_object = ObjectCreator()
print my_object  # <__main__.ObjectCreator object at 0x8974f2c>

但是,Python中的类还远不止如此。类同样也是一种对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。将在内存中创建一个对象,名字就是ObjectCreator。

class ObjectCreator(object):
     pass

这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象,于是你可以对它做如下的操作:

  • 将它赋值给一个变量
  • 拷贝它
  • 为它增加属性
  • 将它作为函数参数进行传递
# 打印一个类,因为它其实也是一个对象
print ObjectCreator  # <class '__main__.ObjectCreator'>

Idef echo(o):
     print o

# 将类做为参数传给函数
echo(ObjectCreator)  # <class '__main__.ObjectCreator'>
print hasattr(ObjectCreator, 'new_attribute')  # False

# 为类增加属性
ObjectCreator.new_attribute = 'foo' 
print hasattr(ObjectCreator, 'new_attribute')  # True
print ObjectCreator.new_attribute   # foo

 # 将类赋值给一个变量
ObjectCreatorMirror = ObjectCreator
print ObjectCreatorMirror()  # <__main__.ObjectCreator object at 0x108551310>

因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。你可以在函数中创建类:

1.使用class关键字,通过return class动态的构建需要的类。 

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo     # 返回的是类,不是类的实例
    else:
        class Bar(object):
            pass
        return Bar
MyClass = choose_class('foo')

# 函数返回的是类,不是类的实例
print MyClass   # <class '__main__.Foo'>

# 你可以通过这个类创建类实例,也就是对象
print MyClass()   # <__main__.Foo object at 0x1085ed950

2.通过type函数构造类

当使用class关键字时,Python解释器自动创建这个对象。但Python仍然提供手动处理的方法——使用type实例化来动态的创建类

type的语法:
type(类名,父类的元组(针对继承的情况,可以为空),类的名称空间(包含属性的字典(名称:值)))

比如下面的代码:
class MyShinyClass(object):
    pass

构建Foo类:
#构建目标代码
class Foo(object):
    bar = True
#使用type构建
Foo = type('Foo', (), {'bar':True}) # 可以使用字典来定义类的属性

例如:

# myclass 包含的代码
code = """
name = "张三"
age = 18
def hello(self):
    print("hello %s" % self.name)
"""
# 类的名字
class_name = "MyClass"
# 类的的父类们
base_classes = (object,)
# 类的名称空间
namespace = {}

exec(code,{},namespace)
res = type(class_name,base_classes,namespace)  # 返回一个类对象

#  创建一个该类的实例
print(res())  # <__main__.MyClass object at 0x000001DB50757780>
print(res.name)  # 张三
print(res.age)  # 18
print(res.hello)  # <function hello at 0x000001DB508F50D0>

在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。

创建模块的过程

  • 1.创建一个空的名称空间
  • 2.执行内部的代码
  • 3.将得到的名字放到名称空间中

控制类的创建过程

  • 1.创建一个元类 (需要继承type)
  • 2.覆盖__init__方法,该方法会将新建的类对象、类名、父类们、名称空间都传进来,可以利用这些信息在做处理
  • 3.对于需要被控制的类,需要指定metaclass 为上面的元类

控制类实例化对象的过程

  • 1.创建一个元类 (需要继承type)
  • 2.覆盖__call__方法,会将正在实例化对象的类、调用类时传入的参数,都传进来
  • 3.在__call__方法中,必须要先编写模板代码
  •   3.1创建空对象 object.__new__(类)
  •   3.2调用类的__init__方法来初始化这个空对象
  •   3.3返回该对象
  • 4.加入你需要控制的逻辑

元类 -> 实例化产生 -> 类 -> 实例化产生 -> 对象

元类:用于产生类的类,(默认)type就是元类

元类就是用来创建类的“东西”。元类就是类的类。

所有的自定义类都是通过type(一个内建函数)实例化得来(继承type),可以通过检查__class__属性来看到这一点。

MyClass = MetaClass()    #元类创建
MyObject = MyClass()     #类创建实例
实际上MyClass就是通过type()来创创建出MyClass类,它是type()类的一个实例;
同时MyClass本身也是类,也可以创建出自己的实例,这里就是MyObject

__metaclass__属性:

你可以在写一个类的时候为其添加__metaclass__属性,定义了__metaclass__就定义了这个类的元类。

语法:
class Foo(object):   #py2
    __metaclass__ = something…


class Foo(metaclass=something):   #py3
    __metaclass__ = something…


定义一个类 :
class Foo(Bar):
    pass

属性查找顺序

在该类并定义的时候,它还没有在内存中生成,想要知道它是否被调用。Python做了如下的操作:

  1. Foo中有__metaclass__这个属性吗?如果有,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象。
  2. 如果Python没有找到__metaclass__,它会继续在父类中寻找__metaclass__属性,并尝试做上一步操作。
  3. 如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做上一步操作。
  4. 如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

那么可以在__metaclass__中放置些什么代码呢?——放置可以创建一个类的东西。

那么什么可以用来创建一个类呢?——type,或者任何使用到type或者子类化type的东西都可以。

自定义元类 —— 控制类的创建过程

  元类的主要目的就是为了当创建类时能够自动地改变类。  

  要控制类的创建过程,只需要找到类所属元类中的__init__方法即可。

 自定义元类控制类的创建过程:
#但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
class MyMeta(type): 
 #第一个参数为创建的类,其余参数,是类名,类的父类元组,类的名称空间
    def __init__(self,class_name,class_bases,namespaces):
        #Python会自动创建属性赋值,不需要我们再重新创建
        #书写逻辑
        if not class_name.istitle(): #书写类名大写打开的逻辑
            raise TypeError("类的名字必须以大写字母开头") #主动抛出异常
        if not self.__doc__ or len((self.__doc__).strip())==0:
            raise TypeError("类必须有文档注释且内容不能为空")

class Student(object,metaclass=MyMeta):  
#Student=Mymeta('Student',(object,),{名称空间})
    """
    xxx
    """
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("%s正在学习。。。"%self.name)
stu=Student("abc",18,"male")  # 会触发Student的类(即元类)中的__call__函数
print(stu.name)
print(stu.__dict__)

# 当不写文档注释或者文档注释为中没有字符时,会报错

# TypeError: 类必须有文档注释且内容不能为空

自定义元类控制类的创建过程的步骤:

  1.  创建一个自定义元类,继承type
  2.  覆盖type方法中的__init__方法,传入的参数为:正在创建的类、类名,类的父类元组,类的名称空间
  3.  加入控制逻辑
  4.  对于需要被控制的类,指定metaclass=自定义的元类

自定义元类 —— 控制类的对象的实例化

  元类的主要目的就是为了当创建类时能够自动地改变类。

  在介绍自定义元类控制类的对象的实例化的过程之前,我们介绍一个内置方法__call__

__call__方法:对象后面加括号,触发执行。

__call__:调用。

  • 在调用对象时,会执行该对象所属类中的__call__方法
  • 调用类时,会执行该类所属元类中的__call__方法。

一般这个方法只会在元类中使用,可以控制类被调用时即类实例化对象过程的一些逻辑判断。

# 自定义一个元类 元类也是一个类   但是需要继承type
class MyMeta(type):

    def __call__(self, *args, **kwargs):  # self 表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数

        print("MyMte中的 call run'")
        print(self,*args,**kwargs)

        # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来

        # 1.创建空对象
        obj = object.__new__(self)
        # 2.调用初始化方法
        self.__init__(obj,*args,**kwargs)
        # self.__init__(obj)
        # 3.得到一个完整的对象
        return obj

class Person(metaclass=MyMeta):  # 修改Person类的元类为MyMeta
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print("call run...")

p = Person("王二狗",66)  # 调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法
print(p)  # 当调用对象时 会执行该对象所属类中的__call__方法
# p() 可以这样调用哟

print(p.name)
print(p.age)

注意

  • __init__构造方法的执行是由创建对象触发的,即:对象 = 类名() ;
  • 而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

自定义元类控制类的对象的实例化过程

#但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
class MyMeta(type): 
    #self是Student这个类,*args和**kwargs是指类实例化对象时传入的参数
    def __call__(self, *args, **kwargs):
        print("MyMeta is running")
        #创建一个空对象
        obj=object.__new__(self)
        #调用初始化函数
        self.__init__(obj,*args,**kwargs)
        #返回对象
        return obj
class Student(object,metaclass=MyMeta):  #Student=Mymeta('Student',(object,),{名称空间})
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("%s正在学习。。。"%self.name)
stu=Student("abc",18,"male")  # 会触发Student的类(即元类)中的__call__函数
print(stu.name)
print(stu.__dict__)

#=======================================================
MyMeta is running
abc
{'name': 'abc', 'age': 18, 'sex': 'male'}

自定义元类的目的

  1. 可以通过__call__来控制对象的创建过程即类实例化对象的过程。
  2. 可以控制类的创建过程。

元类中的__new__

  __new__的作用是创建一个空的类(实例)对象,在对象被创建的时候调用该方法,它是一个类方法。作为一个类对象,必须具备的三个组成部分(类名、父类的元组、名称空间),所以调用type中的__new__来完成组装,得到这个类对象后需要将返回一个实例,以供__init__来使用(自动的调用__init__方法,对实例进行初始化)。如果__new__方法不返回值,或者返回的不是实例,那么它就不会自动的去调用__init__方法。(即必须生成本类的实例才能自动调用本类的__init__方法进行初始化,否则不会自动调用__init__)

  __init__ 方法负责将该实例对象进行初始化,在对象被创建之后调用该方法,在__new__方法创建出一个实例后对实例属性进行初始化。__init__方法可以没有返回值。想为一个类添加一个属性,在__init__中实现就可以了,没有必要重写__new__方法。

   __new__和__init__的区别:__new__比__init__先执行。
 
 看到一个很贴切的比喻(https://www.cnblogs.com/tkqasn/p/6524879.html)
如果将创建实例的过程比作建一个房子。
    class就是一个房屋的设计图,他规定了这个房子有几个房间,每个人房间的大小朝向等。这个设计图就是累的结构
    __new__就是一个房屋的框架,每个具体的房屋都需要先搭好框架后才能进行专修,当然现有了房屋设计才能有具体的房屋框架出来。这个就是从类到类实例的创建。
    __init__就是装修房子的过程,对房屋的墙面和地板等颜色材质的丰富就是它该做的事情,当然先有具体的房子框架出来才能进行装饰了。这个就是实例属性的初始化,它是在__new__出一个实例后才能初始化。
    __call__就是房子的电话,有了固定电话,才能被打电话嘛(就是通过括号的方式像函数一样执行)。
#coding:utf-8
class Foo(object):
    def __new__(cls, *args, **kwargs):
        #__new__是一个类方法,在对象创建的时候调用
        print("excute __new__")
        return super(Foo,cls).__new__(cls)

    def __init__(self,value):
        #__init__是一个实例方法,在对象创建后调用,对实例属性做初始化
        print("excute __init__")
        self.value = value

f1 = Foo(1)
print(f1.value)
f2 = Foo(2)
print(f2.value)

# excute __new__
# excute __init__
# 1
# excute __new__
# excute __init__
# 2
# 可以看出new方法在init方法之前执行

子类如果重写__new__方法,一般依然要调用父类的__new__方法。

单例(一种设计模式/套路)

一个类如果只有一个实例,那么该类称之为单例

如果需要实现的功能相同,且数量众多(并发量大)或者说要处理的数据只有一份时,所有对象的属性都相同时-----使用单例

为什么需要单例:通过这种方法,可以保证功能相同的对象可以在内存中仅创建一个(以__call__控制实例化过程,保证每一次调用都返回一个实例),需要时都去调用,节省内存。

# 单例类定义 
class Foo(object):
 
    __instance = None
 
    @staticmethod
    def singleton():
        if Foo.__instance:
            return Foo.__instance
        else:
            Foo.__instance = Foo()
            return Foo.__instance
 
# 获取实例
obj = Foo.singleton()

对于Python单例模式,创建对象时不能再直接使用:obj = Foo(),而应该调用特殊的方法:obj = Foo.singleton() 。

#     -- 实现元类为CarMeta的类至少有生产日期(production_date)、发动机编号(engine_number)及载客量(capacity)三个基本属性
class CarMeta(type):
    def __call__(self, *args, **kwargs):
        print(args)
        if len(args)<3:
            raise ValueError("  ")
        obj = object.__new__(self)
        self.__init__(obj,*args,**kwargs)

        if not ("production_date"in obj.__dict__ and "engine_number"in obj.__dict__ and "capacity"in obj.__dict__):
            raise ValueError(" ")
        return obj

class BigCar(metaclass=CarMeta):
    def __init__(self,production_date,engine_number,capacity):
        self.production_date=production_date
        self.engine_number = engine_number
        self.capacity=capacity

car=BigCar("2018 12 21","xxxxxx",6)
print(car)
自定义轿车元类CarMeta

单例模式的要点:

  1. 某个类只能有一个实例
  2. 它必须自行创建这个实例
  3. 它必须自行向整个系统提供这个实例。

实现单例的方式:

  1. 使用__new__方法实现单例
  2. 使用元类(metaclass)实现单例
  3. 使用模块实现单例
  4. 使用装饰器(decorator)实现单例

1.使用__new__方法实现单例

class MyClass(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(MyClass, cls).__new__(cls, *args, **kwargs)
        return cls._instance
 
class HerClass(MyClass):
    a = 1

one = HerClass()
two = HerClass()
print(one == two)   #True
print(one is two)   #True
print(id(one), id(two)) #42818864 42818864

在上面的代码中,我们将类的实例和一个类变量 _instance 关联起来,如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance

重写了__new__方法,继承MyClass类。主要用于需要对类的结构进行改变的情况。

2.使用元类(metaclass)实现单例

元类(metaclass)可以控制类的创建过程,它主要做三件事:

  • 拦截类的创建
  • 修改类的定义
  • 返回修改后的类

使用元类实现单例模式的代码如下:

class Singleton(type):
    _instances = {}
 
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]
 
 
# Python2
# class MyClass(object):
#     __metaclass__ = Singleton
 
# Python3
class MyClass(metaclass=Singleton):
   pass

优点: 

  1. 实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
  2. 灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。

 缺点: 

  1. 开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
  2. 可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
  3. 对象生存期:不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。 
class MyMeta(type):
    obj=None
    def __call__(self, *args, **kwargs):
        if not MyMeta.obj:
            #创建空对象
            obj=object.__new__(self)
            #调用初始化函数
            self.__init__(obj,*args,**kwargs)
            MyMeta.obj=obj
        return MyMeta.obj
class Printer(metaclass=MyMeta):
    obj=None
    def __init__(self,name,brand,type):
        self.name=name
        self.brand=brand
        self.type=type
    def printing(self):
        print("正在打印。。。")
p1=Printer("ES005","爱普生","彩色打印机")
p2=Printer("ES005","爱普生","彩色打印机")
p3=Printer("ES005","爱普生","彩色打印机")
print(p1)
print(p2)
print(p3)

#============================================

<__main__.Printer object at 0x000002740AE4AF60>
<__main__.Printer object at 0x000002740AE4AF60>
<__main__.Printer object at 0x000002740AE4AF60>
自定义元类实现单例模式

3.使用模块实现单例

Python 的模块就是天然的单例模式。因为模块在第一次导入时,会生成 .py文件,当第二次导入时,就会直接加载 .py文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

#tests1.py
class MyClass(object):
    def foo(self):
        print('MyClass.foo')
my_class_obj=MyClass()

# 将上面的代码保存在文件 tests1.py 中,然后导入使用:

from .tests1 import my_class_obj
my_class_obj.foo()

4.使用装饰器(decorator)实现单例

装饰器(decorator)可以动态地修改一个类或函数的功能。所以可以使用装饰器来装饰某个类,使其只能生成一个实例

from functools import wraps
 
def singleton(cls):
    instances = {}
 
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance
 
@singleton
class MyClass(object):
    a = 1

定义一个装饰器 singleton,返回一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls作为 key,cls(*args, **kw) 作为 value 存到 instances 中,否则,直接返回 instances[cls]

#!/usr/bin/env python
#coding:utf-8
from wsgiref.simple_server import make_server
————————单例类定义——————————————
class DbHelper(object):

    __instance = None

    def __init__(self):
        self.hostname = '1.1.1.1'
        self.port = 3306
        self.password = 'pwd'
        self.username = 'root'

    @staticmethod
    def singleton():
        if DbHelper.__instance:
            return DbHelper.__instance
        else:
            DbHelper.__instance = DbHelper()
            return DbHelper.__instance

    def fetch(self):
        # 连接数据库
        # 拼接sql语句
        # 操作
        pass

    def create(self):
        # 连接数据库
        # 拼接sql语句
        # 操作
        pass

    def remove(self):
        # 连接数据库
        # 拼接sql语句
        # 操作
        pass

    def modify(self):
        # 连接数据库
        # 拼接sql语句
        # 操作
        pass


class Handler(object):

    def index(self):
        obj =  DbHelper.singleton()
        print id(single)
        obj.create()
        return 'index'

    def news(self):
        return 'news'


def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    temp = url.split('/')[1]
    obj = Handler()
    is_exist = hasattr(obj, temp)
    if is_exist:
        func = getattr(obj, temp)
        ret = func()
        return ret
    else:
        return '404 not found'

if __name__ == '__main__':
    httpd = make_server('', 8001, RunServer)
    print "Serving HTTP on port 8001..."
    httpd.serve_forever()
Web应用实例--单例模式

 

posted @ 2019-05-30 10:45  呔!妖精。。。  阅读(129)  评论(0编辑  收藏  举报