python学习(7)之面向对象(类,面向对象特性,元类,单例模式)

2.2 python面向对象

2.2.1 创建类

类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
方法:类中定义的函数。
类的构造方法__init__():类有一个名为 init() 的特殊方法(构造方法),该方法在类实例化时会自动调用。
实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
实例化:创建一个类的实例,类的具体对象。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。

# 定义类
class MyClass(object):
    def __init__(self, name):
        self.name = name
    def __new__(self, *args, **kwargs):
        return object.__new__(self)


# 根据类创建对象
# 1 执⾏类的new⽅法,创建空对象 【构造⽅法】 {}
# 2 执⾏类的init⽅法,初始化对象 【初始化⽅法】{name:"lLiverpool"}
obj = MyClass("liverpool")

对象是基础类创建的,是先运行了类的new方法,创建了空对象,然后在调用类的构造方法,初始哈创建的空对象。类是由type创建。下面介绍创建类的第二种方式。

# 传统⽅式创建类
class MyClass1(object):
    v1 = 123
    def func(self):
        return 666


# ⾮传统⽅式创建类
# - 创建类型 	
# - 类名
# - 继承类
# - 成员
Myclass2 = type("MyClass2", (object,), {"v1": 123, "func": lambda self: 666})
# ⾮传统⽅式创建对象
obj = Myclass2()
# ⾮传统⽅式调⽤v1的变量
print(obj.v1)
2.2.2 面向对象的特性
2.2.2.1 多态

多态 一般 是通过继承和方法重写 实现 , 多个子类 继承 同一个父类 ,这些子类对象重写父类的 方法 , 实现不同的逻辑 ,

为父类类型变量赋值不同的类对象 , 当调用被重写的父类方法时 , 执行不同的逻辑 , 此时就实现了多态 ;

子类重写父类的方法 , 这意味着子类可以定义与父类相同名称的方法 , 但实现方式可以不同 ;

当通过子类对象调用该方法时 , 将执行子类的方法 , 而不是父类的方法 ;

这种行为使得子类可以对相同的消息做出不同的响应 , 实现了多态 ;

举例如下:

class Animal:
    name = "Animal"
    age = 0
    def make_sound(self):
        print("动物发音")
class Dog(Animal):
    def make_sound(self):
        super().make_sound() # super().父类方法表示 调用父类的所定义的方法:super.__init__() 表示调用父类的初始化方法
        print("汪汪")
class Cat(Animal):
    def make_sound(self):
        print("喵喵")
animal = Animal()
animal.make_sound() # 动物发音
dog = Dog()
dog.make_sound() # 汪汪
cat = Cat()
cat.make_sound() # 喵喵
2.2.2.2 抽象类

抽象类:父类只定义空方法 , 方法体是 pass , 没有具体实现 ;父类只定义有哪些方法子类负责实现具体的方法逻辑这种 父类 , 就是 " 抽象类 " ;方法体为空 , 也就是 pass 的方法 , 称为 " 抽象方法 " ;有 " 抽象方法 " 的类 , 称为 抽象类 ;

class Animal(object): # 抽象类
    def __init__(self):
        self.age = 1 # 可以存在属性
    def make_sound(self): # 抽象方法
        pass
class Dog(Animal):
    def make_sound(self):
        print("汪汪")
class Cat(Animal):
    def make_sound(self):
        print("喵喵")
dog = Dog()
dog.make_sound()
cat = Cat()
cat.make_sound()

这种抽象方法的实现有它的弊端,如果你写一个类继承抽象类,但是忘记实现抽象方法,异常只有在你真正使用所定义的抽象函数才会抛出来,还有一种方式可以让错误更早的触发,使用Python提供的abc模块,对象被初始化之后就可以抛出异常。只有强制定义抽象方法后才会取消异常。

import abc
class Animal(object): # 抽象类
    def __init__(self):
        self.age = 1

    @abc.abstractmethod
    def make_sound(self): # 抽象方法
        raise NotImplementedError

class Dog(Animal):
    def make_sound(self):
        print("汪汪")
2.2.2.3 静态方法

静态方法:通过类名. 调用静态方法不需要创建对象,就可以直接调用。

# 静态方法
class Cat(object):
    # 不访问实例属性/类属性
    # 静态方法不需要传递第一个参数self
    @staticmethod
    def call():
        print('喵喵~')
# 通过类名. 调用静态方法
# 不需要创建对象,就可以直接调用
Cat.call()
2.2.2.4 私有属性和私有方法

私有属性和私有方法:Python中并没有有效限制访问任何实例变量或方法的机制,但是Python规定了使用单/双下划线为变量/方法的名称前缀,以模拟受保护和专用访问说明符的行为。默认情况下,Python类中所有成员都是公共的,可以从类环境之外访问任何成员

# 私有属性和私有方法
class Car(object):
    def __init__(self, age, color, brand="奥迪"):  # 构造方法
        self.__brand = brand  # 私有属性 外部不能调用 只能在类中使用
        self.age = age  # 实例属性
        self.color = color  # 实例属性
    def __voice(self):  # 私有方法 只能在类中使用
        print(number"车的年龄{self.age}")
car = Car(9, "red")
2.2.2.5 类属性和类方法

类属性和类方法:类也是一个特殊的对象,类对象在内存中只有一份,通过他可以创建出很多个对象实例,除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法,这就是所谓的就是的类属性和类方法。

类属性:就是给类对象定义的属性。类属性不会用于记录具体的对象特征。使用赋值语句在class关键字下方就可以定义类属性。类属性也可以直接在外面调用。

class Tool(object):
    # 1.使用赋值语句定义类属性,记录所有的工具数量
    count = 0
    def __init__(self, name):
        self.name = name
        # 让类属性的值 +1
        Tool.count += 1
# 创建工具对象(对象在创建的时候,会自动调用初始化方法)
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('水桶')
print(Tool.count) # 输出3 可以直接调用类的属性(类似于静态属性)
class Toy(object):
    # 使用赋值语句定义类属性,记录所有玩具的数量
    count = 0
    @classmethod  # 定义于类方法
    def show_toy_count(cls):
        # 在类方法的内部,可以直接访问类属性或调用类方法
        print('玩具对象的数量 %d' % cls.count)
    def __init__(self, name):
        self.name = name
        Toy.count += 1
# 创建玩具对象
toy1 = Toy('乐高')
toy2 = Toy('玩具车')
# 调用类方法
Toy.show_toy_count() # 可以类似于静态方法的调用
2.2.2.6 单例

单例

单例是一种设计模式,应用该模式的类只会生成一个实例。

单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例。

如果实例不存在,会创建一个实例;如果已存在就会返回这个实例。因为单例是一个类,所以你也可以为其提供相应的操作方法,以便于对这个实例进行管理。

用new方法新建单例:

# 返回单例
class MyInstance(object):
    instance = None
    def __new__(cls, *args, **kwargs):
        # 第一个参数cls:哪一个类调用就传递哪一个类
        # 第二个参数*args:多值的元组参数
        # 第三个参数**kwargs:多值的字典参数
        # 1.创建对象的时候,new方法会被自动调用
        # print '创建对象,分配空间' # 重写了父类的方法
        # 2.为对象分配空间
        # 注意:__new__方法是一个静态方法,在调用的时候,第一个参数为cls
        if cls.instance is None:
            # 调用父类的方法,为第一个对象分配空间
            cls.instance = object.__new__(cls)
        # 3.返回对象的引用
        return cls.instance
    def __init__(self):
        print('初始化播放器')
# 创建播放器对象
MyInstance1 = MyInstance()
print(MyInstance1) 
MyInstance2 = MyInstance()
print(MyInstance2) # 返回都是返回的同一个的对象地址

使用迭代器构建单例:

# 使用迭代器构建单例
def Singleton(cls):
    _instance = {}
    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return _singleton
@Singleton
class Cls(object):
    def __init__(self):
        pass
cls1 = Cls()
cls2 = Cls()
print(id(cls1) == id(cls2))
2.2.3 元类的定义

Python中一切皆为对象,我们所定义的类也是type类所实例的一个对象。type为对象的顶点,所有对象都创建自type,包括其本身;object为类继承的顶点,所有类都继承自object,包括其本身。type是object的类型,同时,object又是type的超类

类本身不过是一个名为type类的实例。用户自定义类,只不过是type类的__call__运算符的重载。当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type__call__运算符。

自定义类的时候会执行这个代码:

class = type(classname, superclasses, attributedict) # 就是type 的__call__运算符重载

Python 调用 type__call__运算符,又会调用以下两个代码

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

class type(name, bases, dict)参数详解:

1、使用1个参数,返回对象的类型。

  • 就像object.__class__

2、使用3个参数,返回一个新类型对象。本质上,这是类声明的一种动态形式。

  • 参数name是一个字符串,表示类名称,并记录为__name__属性;
  • 参数bases是一个元组,一个个记下基础类,并记录为__bases__属性;
  • 参数dict是一个字典,包含类本体的命名空间并被赋值到标准字典。并记录为__dict__属性。

metaclass是type的子类,通过替换type的__call__运算符重载机制,一旦把一个类型MyClass的 metaclass 设置成MyMeta,MyClass就不再由原生的type创建,而是会调用MyMeta 的__call__运算符重载。

class = type(classname, superclasses, attributedict) 
# 变为了
class = MyMeta(classname, superclasses, attributedict)

举例代码如下:

class MyType(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("this is my type init")

    def __new__(cls, *args, **kwargs):
        new_cls = super().__new__(cls, *args, **kwargs)
        print("this is my type new")
        return new_cls

    def __call__(self, *args, **kwargs):
        print("this is my type call")
        # 调⽤⾃⼰的那个类 __new__ ⽅法去创建对象
        empty_object = self.__new__(self)
        # 调⽤你⾃⼰的__init__ ⽅法取初始化
        self.__init__(empty_object, *args, **kwargs)
        return empty_object


class Foo(object, metaclass=MyType):
    def __init__(self, name):
        self.name = name
        print("this is Fool init")

    def __new__(self, *args, **kwargs):
        print("this is Foo new!")
        return object.__new__(self)

    def __call__(self, * args, ** kwargs):
        print("this is Foo call!")

v1 = Foo("Liverpool")
v1() # 调用的是v1中的__call__方法
'''输出如下:
this is my type new
this is my type init
this is my type call
this is Foo new!
this is Fool init
this is Foo call!
'''

对上述代码进行分析:首先Foo("Liverpool"),调用是MyType这个所自定义的元类,但是这个MyType是由于type这个原始类所继承定义的,所以需要调用type这个类所定义的__call__函数,但是MyType这个所自定义的元类已经重载了type类所定义的__new__方法和__init__方法,所以先调用MyType这两个方法所创造出MyType这个元类对象,然后在执行MyType所定义的__call__函数,用来生成Foo这个类,所定义的初始化方法都是来自Foo类自己所重载的__new__方法和__init__方法。

v1():表示执行的是v1这个对象实例的__call__方法,也就是Foo所定义的__call__方法。

2.2.4 元类的应用

首先我们要明确一点,类都是由元类创建的,并且元类都是由type创建的,但是为了区分可以把类说成由metaclass这个关键字所对应的的元类创建的。如果某一个类是继承某个类,父类是由metaclass创建的,那么该类就是由metaclass所定义的 元类来创建的。

from wtforms import Form
from wtforms.fields import simple


class LoginForm(Form):
    name = simple.StringField(label='⽤户名', render_kw={'class': 'form-control'})
    pwd = simple.PasswordField(label='密码', render_kw={'class': 'form-control'})

form = LoginForm()
print(form.name)  # 类变量
print(form.pwd)  # 类变量

首先form这个实例对象,是由LoginForm这个类所创建的,LoginForm这个类是继承来自Form这个类,所以接下来我们分析Form的源代码如下:

class Form(BaseForm, metaclass=FormMeta):
	pass

可以比较明确的看出,Form这个类是由BaseForm这个类继承而来,FormMeta这个元类创建的,继续分析FormMeta这个元类

class FormMeta(type):
    def __init__(cls, name, bases, attrs):
        type.__init__(cls, name, bases, attrs)
        cls._unbound_fields = None
        cls._wtforms_meta = None

    def __call__(cls, *args, **kwargs):
        if cls._unbound_fields is None:
            fields = []
            for name in dir(cls):
                if not name.startswith("_"):
                    unbound_field = getattr(cls, name)
                    if hasattr(unbound_field, "_formfield"):
                        fields.append((name, unbound_field))
            # We keep the name as the second element of the sort
            # to ensure a stable sort.
            fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
            cls._unbound_fields = fields

        # Create a subclass of the 'class Meta' using all the ancestors.
        if cls._wtforms_meta is None:
            bases = []
            for mro_class in cls.__mro__:
                if "Meta" in mro_class.__dict__:
                    bases.append(mro_class.Meta)
            cls._wtforms_meta = type("Meta", tuple(bases), {})
        return type.__call__(cls, *args, **kwargs)

可以看出FormMeta是个由type创建的元类。

所以分析上述代码可以得出:LoginForm其实是由FormMeta 创建的。创建LoginForm类时,会执⾏FormMeta__new____init__(是执行了type元类的__call__方法,调用FormMeta 重载的__new____init__方法),内部在类中添加了两个类变量 _unbound_fields_wtforms_meta;form = LoginForm()会调用LoginForm类去创建对象。会调用FormMeta.__call___ ⽅法 。会调用LoginForm中的__new__ 方法去创建对象,__init__去初始化对象。

2.2.5 用元类定义单例模式
class SingletonType(type):
    def __init__(cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls.instance = None

    def __call__(cls, *args, **kwargs):
        # 1判断是否有对象, 有不创建,没有才创建
        if not cls.instance:
            cls.instance = cls.__new__(cls)
        # 2 调用自己的类进行初始化
        cls.__init__(cls.instance, *args, **kwargs)
        return cls.instance
    
class Singleton(object, metaclass=SingletonType):
    pass

class Foo(Singleton):
    pass

v1 = Foo()
v2 = Foo()
print(v1)
print(v2)
  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值