当前位置 : 主页 > 编程语言 > python >

Python中面向对象(学习笔记)

来源:互联网 收集:自由互联 发布时间:2022-06-18
文章目录 ​​一、面向过程与面向对象​​ ​​简述​​ ​​面向过程编程​​ ​​面向对象编程​​ ​​二、类和对象​​ ​​类​​ ​​对象(实例)​​ ​​类和对象的关



文章目录

  • ​​一、面向过程与面向对象​​
  • ​​简述​​
  • ​​面向过程编程​​
  • ​​面向对象编程​​
  • ​​二、类和对象​​
  • ​​类​​
  • ​​对象(实例)​​
  • ​​类和对象的关系​​
  • ​​三、类的定义​​
  • ​​四、类中的成员​​
  • ​​类属性​​
  • ​​实例函数(对象函数,成员函数)​​
  • ​​实例函数和普通函数的区别​​
  • ​​关于self​​
  • ​​创建对象(实例化对象)​​
  • ​​调用类中的实例函数​​
  • ​​动态绑定属性和限制绑定​​
  • ​​动态绑定属性​​
  • ​​限定绑定​​
  • ​​内存中的对象​​
  • ​​构造函数​​
  • ​​工作原理​​
  • ​​给 `__init__()` 设置参数​​
  • ​​析构函数​​
  • ​​1. 将对象定义为全局变量,程序执行完毕,对象自动被销毁​​
  • ​​2. 将对象定义为局部变量,当指定的函数执行完毕,则对象随着会被自动销毁​​
  • ​​3. 强制销毁对象,什么时候del对象,则什么时候执行析构函数 `__del__()`​​
  • ​​五、封装​​
  • ​​概念​​
  • ​​私有属性​​
  • ​​1. 概念​​
  • ​​2. 属性未被私有化​​
  • ​​3. 属性被私有化​​
  • ​​4. 暴露给外界访问的函数​​
  • ​​5. @property装饰器​​
  • ​​私有函数​​
  • ​​1. 函数未被私有化​​
  • ​​2. 函数被私有化​​
  • ​​六、继承​​
  • ​​单继承​​
  • ​​1. 基本使用​​
  • ​​2. 继承中的 `__slots__` 属性​​
  • ​​3. 继承中的类属性​​
  • ​​4. 继承的特点​​
  • ​​5. 继承的优缺点​​
  • ​​多继承​​
  • ​​1. 基本使用​​
  • ​​2. 继承的 `__mro__` 属性​​
  • ​​3. 多继承中 super 本质(继承树问题)​​
  • ​​函数重写​​
  • ​​1. 自定义函数​​
  • ​​2. 系统函数​​
  • ​​七、多态​​
  • ​​场景​​
  • ​​实现​​
  • ​​最终实现​​
  • ​​多态总结​​
  • ​​八、其他​​
  • ​​对象的内置属性​​
  • ​​1. `__slots__`​​
  • ​​2. `__doc__`​​
  • ​​3. `__dict__`​​
  • ​​4. `__module__`​​
  • ​​5. `__class__`​​
  • ​​对象的内置函数​​
  • ​​1. `id()`​​
  • ​​2. `type()`​​
  • ​​3. `isinstance()`​​
  • ​​4. `dir()`​​
  • ​​5. `issubclass(子类, 父类)`​​
  • ​​类方法​​
  • ​​静态方法​​
  • ​​九、单例设计模式​​
  • ​​概念​​
  • ​​应用场景​​
  • ​​实现​​
  • ​​1. 模块​​
  • ​​2. `__new__`​​
  • ​​3. 装饰器​​
  • ​​十、枚举类​​
  • ​​使用枚举类的好处​​
  • ​​创建枚举类​​
  • ​​查看枚举类型​​
  • ​​重复的枚举类型​​
  • ​​确保枚举类型的唯一​​
  • ​​IntEnum​​
  • ​​枚举类的其他特性​​
  • ​​十一、接口类​​
  • ​​十二、抽象类​​


一、面向过程与面向对象

简述

面向过程:根据业务逻辑从上到下写代码。

面向对象:将变量与函数绑定到一起,分类进行封装,每个程序只要负责分配给自己的分类,这样能够更快速的开发程序,减少了重复代码。

面向过程编程

面向过程编程的关注点在于​怎么做​

  • 把完成某一个需求的 所有步骤 从头到尾 逐步实现
  • 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
  • 最后完成的代码,就是顺序地调用 不同的函数

特点:

  • 注重步骤与过程,不注重职责分工
  • 如果需求复杂,代码会变得很复杂
  • 开发复杂项目,没有固定的套路,开发难度很大

面向对象编程

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)和面向过程编程,是两种不同的编程方式。


相比较函数,面向对象是更大的封装,根据职责在 一个对象中封装多个方法


面向对象编程的关注点在于​谁来做​

  • 在完成某一个需求前,首先确定职责,也就是要做的事情(方法)
  • 根据 职责 确定不同的 对象,在对象内部封装不同的方法(多个)
  • 最后完成的代码,就是顺序地调用不同对象的相应方法。

特点:

  • 注重 对象 和 职责,不同的对象承担不同的职责。
  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。

二、类和对象

类是对一群具有相同 特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用。

  • 特征其实就是一个变量,在类里我们称之为​属性​。
  • 行为其实就是一个函数,在类里我们称之为​方法​。
  • 类其实就是由​属性​和​方法​组成的一个抽象概念。

对象(实例)

对象是由类创建出来的一个具体存在,可以直接使用。由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法。在开发中,应该先有类,在类里定义好属性和行为,再根据类来创建对象。

类和对象的关系

  • 类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。
  • 使用同一个类,能够创建出很多对象。
  • 类中定义了什么属性和方法,对象中就有什么属性和方法。
  • 不同对象对应的属性值也会不同。

三、类的定义

class 类名():
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass

在实际项目开发中,建议类的定义和模块结合使用(一个模块中定义一个类),相同或者相似的类可以使用包进行管理。

四、类中的成员

类属性

方式一:通过 ​​类名.类属性​​ 访问

方式二:通过 ​​实例对象.类属性​​ 访问

class Person(object):
# 类属性
num = 10


p = Person()

# 方式一
print(Person.num)

# 方式二
print(p.num)

实例函数(对象函数,成员函数)

class Person():
# 实例函数,对象函数,成员函数
def eat(self, food):
print("eating" + food)
实例函数和普通函数的区别
实例函数的形参列表第一个参数是self,而普通则不是。
关于self
self表示当前对象
1. self并不是一个关键字,其实可以是任意的标识符,为了表达代表的是当前对象自己,习惯用self
2. 调用实例函数的时候,self不需要手动传参,系统会自动传递当前的对象
3. 哪个对象调用了方法,方法里的self指的就是谁。
通过 self.属性名 可以访问到这个对象的属性。
通过 self.方法名() 可以调用这个对象的方法。
创建对象(实例化对象)

语法:​​变量名 = 类名(初始值)​​ ,目前初始值省略。

class Person(object):
def eat(self, food): # 实例方法有一个参数self,指的是实例对象
print(food)

p1 = Person()
p2 = Person()

类其实就是自定义的数据类型,创建对象(实例化对象)的过程其实就是定义变量过程。

通过一个普通的类可以创建无数个对象,每个对象在内存中都会开辟新的空间。

调用类中的实例函数

方式一:使用 ​​对象名.方法名(参数)​​ 调用的方式,不需要传递self。

方式二:使用 ​​类名.方法名(self, 参数)​​ 的方式,不会自动给 self 传参,需要手动的指定self。

class Person(object):
def eat(self, food): # 实例方法有一个参数self,指的是实例对象
print(food)


p1 = Person()

# 方式一
p1.eat('蘸水面')

# 方式二
Person.eat(p1, '蘸水面')

动态绑定属性和限制绑定

动态绑定属性

语法:​​对象.属性 = 初始值​​

class Person(object):
def eat(self, food): # 实例方法有一个参数self,指的是实例对象
print(food)


p1 = Person()
p1.name = 'Lucy'
p1.age = 18

缺点:绑定的属性的数量没有上限,绑定的属性的名称没有限制,在实际项目开发中,需要进行限制绑定。

限定绑定

语法:​​__slots__ = (属性1, 属性2,…………)​​

class Person(object):
__slots__ = ('name', 'age')

def eat(self, food): # 实例方法有一个参数self,指的是实例对象
print(food)


p1 = Person()
p1.name = 'Lucy'
p1.age = 18
# p1.height = 178 # AttributeError: 'Person' object has no attribute 'height'

内存中的对象

class Person(object):
num = 10

p11 = Person()
p12 = Person()
# p11.num 和 p12.num访问的是同一个num,num来自于类属性,当前类持有,通过该类创建的所有的对象都持有
print(p11.num is p12.num) # True
print(p11.num, p12.num) # 10 10class Person(object):
num = 10

p13 = Person()
p13.num = 20
p14 = Person()
p14.num = 20
# p13.num和 p14.num访问的不是同一个num,num被称为对象属性,仅限当前对象持有
print(p13.num is p14.num) # True
del p13.num
print(p13.num, p14.num) # 10 20

构造函数

在代码比较复杂的情况下,使用动态绑定属性的方式创建对象,就不太好了。这时需要一个名称为​​__init__​​ 的方法,该特殊的方法被称为构造函数,主要用于创建对象并将对象的数据初始化。

构造函数,也被称为构造器,指的是当创建对象的时候,被自动调用的函数。

严格意义讲,​​__new__​​ 不属于构造函数。

工作原理
class Book(object):
# cls和self都不是关键字,可以是一个普通的标识符
# cls全称class,表示当前类
# __new__属于类方法
def __new__(cls, *args, **kwargs):
print("new被调用了")
# 注意:__new__必须有返回值,并且返回一个实例
# super(Book,cls).__new__(cls)表示当前类创建出来的一个实例
return super(Book, cls).__new__(cls)

# self表示当前对象
# __init__属于实例函数
def __init__(self):
print("init被调用了")


b1 = Book()
print(b1) # <__main__.Book objcet at 0x6572846>
# 如果类中重写__str__,则不打印地址工作原理:
1. __new__:创建对象的时候首先自动调用__new__,它的作用就是创建实例,然后将该实例返回
2. __init__:当实例创建完毕之后被调用的,然后通过__init__给实例设置初始值
3. __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__的第一个参数self,然后__init__给这个实例设置一些参数,__init__不需要返回值。
给 ​​__init__()​​ 设置参数
class Student(object):
__slots__ = ("name", "age", "hobby", "score")

def __init__(self, a, b, c):
self.name = a
self.age = b
self.hobby = c
self.score = 60

但是,一般形参列表中变量的名称和规定的属性名一致,所以建议使用下面的书写方式。

class Student(object):
__slots__ = ("name", "age", "hobby", "score")

def __init__(self, name, age, hobby):
self.name = name
self.age = age
self.hobby = hobby
self.score = 60

析构函数

当对象被销毁的时候自动调用的函数,称为析构函数,析构函数为 ​​__del__()​​ 。

1. 将对象定义为全局变量,程序执行完毕,对象自动被销毁
class Animal(object):
def __init__(self):
print("init被调用了")

def __del__(self):
print("del被调用了")


a1 = Animal()
print("over")

"""
init被调用了
over
del被调用了
"""
2. 将对象定义为局部变量,当指定的函数执行完毕,则对象随着会被自动销毁
class Animal(object):
def __init__(self):
print("init被调用了")

def __del__(self):
print("del被调用了")


def func():
a2 = Animal()
print("函数内部")


func()
print("over")

"""
init被调用了
函数内部
del被调用了
over
"""
3. 强制销毁对象,什么时候del对象,则什么时候执行析构函数 ​​__del__()​​
class Animal(object):
def __init__(self):
print("init被调用了")

def __del__(self):
print("del被调用了")


a3 = Animal()
del a3
print("over")

"""
init被调用了
del被调用了
over
"""

五、封装

概念

广义的封装:函数的定义和类的提取,都是封装的体现。

狭义的封装:在面向对象编程中,一个类的某些属性,在使用的过程中,如果不希望被外界​直接​访问,就可以将该属性封装(将不希望被外界直接访问的属性私有化private,该属性只能被当前类持有,此时可以给外界暴露一个访问的函数即可)。

封装的本质:就是属性私有化的过程。

封装的好处:提高了数据的安全性,提高了数据的复用性。

私有属性

1. 概念

公开属性:可以在类以外的任何地方直接访问。

私有属性:只能在类的内部被直接访问,在对象属性的前面添加两个下划线表示是私有属性。

2. 属性未被私有化
class Person(object):
def __init__(self, name, age):
# 公开属性
self.name = name
self.age = age


p1 = Person("jack", 10)
# 直接访问
print(p1.name, p1.age) # jack 10

# 修改
p1.name = "tom"
p1.age = 19
print(p1.name, p1.age) # tom 19
3. 属性被私有化

工作原理:一个属性一旦被私有化,在底层形成了 ​​_类名__属性名​​ 的属性名 ,但是不建议使用。私有化属性在底层的存在形式根据操作系统或者Python解释器的不同会有所差别,如果直接使用此种方式访问,违背了Python跨平台的特点。

class Person(object):
def __init__(self, name, age):
self.__name = name
self.__age = age


p = Person("jack", 10)
# print(p.name,p.age) #AttributeError: 'Person2' object has no attribute 'name'
# print(p.__name) #AttributeError: 'Person2' object has no attribute '__name'
print(p._Person__name) # jack
4. 暴露给外界访问的函数

在类外面访问属性,无非涉及到两个操作:获取值,修改值。

class Person(object):
def __init__(self, name, age):
self.__name = name
self.__age = age

# 返回被私有化属性的值
def getname(self):
return self.__name

def setname(self, name):
self.__name = name

def getage(self):
return self.__age

def setage(self, age):
if age < 0:
age = abs(age)
self.__age = age


p = Person("jack", 10)
# 获取值
r1 = p.getname()
print(r1) # jack
# 修改值
p.setname("tom")
r2 = p.getname()
print(r2) # tom

p.setage(-19)
print(p.getage()) # 19
5. @property装饰器
  • 单独使用
  • ​​@property​​​ 将一个函数换为属性调用,为了简化函数的调用,函数名本身表示指定函数的返回值,如:​​show()​​​使用​​@property​​​修饰,则 ​​show()​​​ 的调用:​​对象名.show​​ 。

    注意:如果函数被​​@property​​修饰,最好设置返回值。

    class Check(object):
    @property
    def show(self):
    # 如果函数被@property修饰,最好设置返回值
    return 'hello'


    c = Check()
    # c.show() #TypeError: 'str' object is not callable
    print(c.show) # hello
  • ​​@property​​​ 和​​@xxx.setter​​
  • 为了简化函数的调用,Python中通过 ​​@property​​ 和 ​​@xxx.setter​​ 分别修改两个函数。

    注意:

    • 两个函数的函数名尽量和被私有化的属性名保持一致
    • ​​@xxx.setter​​​ 中的​​xxx​​​ 需要和被​​@property​​ 修饰的函数名保持一致
    • ​​@property​​​ 修饰的函数用于获取值,​​@xxx.setter​​ 修饰的函数用于修改值
    • ​​@property​​​ 和​​@xxx.setter​​ 并不是需要同时出现,根据自己的需求进行选择
    • ​​@property​​​出现,​​@xxx.setter​​ 可以不出现
    • ​​@xxx.setter​​​出现,​​@property​​ 必须出现
    class Person(object):
    def __init__(self, name, age):
    self.__name = name
    self.__age = age

    # 获取值:将被私有化属性的值返回
    @property
    def name(self):
    return self.__name

    # 修改值:设置参数
    @name.setter
    def name(self, name):
    self.__name = name

    @property
    def age(self):
    return self.__age

    @age.setter
    def age(self, age):
    if age < 0:
    age = abs(age)
    self.__age = age


    # 将函数当做属性访问
    p = Person("tom", 17)
    print(p.name) # tom
    p.name = "bob"
    print(p.name) # bob

    私有函数

    在类中,对象函数之间相互调用,语法:​​self.函数名​​ 。

    1. 函数未被私有化
    class Person1(object):
    def func1(self):
    print("111")
    # 注意:在类中,对象函数之间相互调用,语法:self.函数
    self.func2()

    def func2(self):
    print("222")

    def show(self):
    print("show")


    p1 = Person1()
    p1.func1()
    # 111
    # 222
    p1.show()
    # show
    2. 函数被私有化
    class Person2(object):
    def func1(self):
    print("111")
    # 间接调用
    self.__show()

    def func2(self):
    print("222")

    def __show(self):
    print("show")


    p2 = Person2()
    p2.func2() # 222
    # p2.show() # 报错
    # p2.__show() # 报错
    # p2._Person2__show() # show
    p2.func1()
    # 111
    # 222

    六、继承

    如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类公共的部分。被抽取出来的类称之为父类(超类、基类),两个或两个以上的类称之为子类 (派生类),他们之间的关系是 ​子类继承自父类​ 或者 ​父类派生了子类​。

    单继承

    1. 基本使用

    子类之继承一个父类,被称为单继承。

    语法:

    class 子类类名(父类类名):
    类体

    注意:object 是 Python 中所有类的根类。

    class Person(object):
    def __init__(self, name, age, gender):
    self.name = name
    self.age = age
    self.gender = gender

    def eat(self):
    print("eating")

    def __show(self):
    print("showing")
  • ​在子类中未定义构造函数,则创建子类对象默认调用父类中的构造函数​
  • class Doctor(Person):
    pass


    d = Doctor("张医生", 30, "male")

    # 注意:子类对象可以直接调用父类中未被私有化的函数
    d.eat() # eating
    d._Person__show() # showing 不建议这样使用
    d._Doctor__show() # AttributeError: 'Doctor' object has no attribute '_Doctor__show'
  • ​在子类中定义构造函数,则创建子类对象调用的是子类中的构造函数​
  • # 有参数时
    class Teacher(Person):
    def __init__(self, height):
    self.height = height


    t = Teacher(180)
    print(t.height) # 180
    # print(t.name, t.age, t.gender) # AttributeError: 'Teacher' object has no attribute 'name'
    t.eat() # eating



    # 无参数时
    class Lawyer(Person):
    def __init__(self):
    print("lawyer~~~init")


    la = Lawyer() # lawyer~~~init

    # print(la.name) # AttributeError: 'Lawyer' object has no attribute 'name'
    la.eat() # eating
  • ​在子类的构造函数中调用父类的构造函数​
  • 方式一:
    super(当前类, self).__init__(参数列表)

    方式二:
    super().__init__(参数列表)

    方式三:
    父类名.__init__(self, 参数列表)

    ​注意:在单继承中,三种方式都可以使用;在多继承中,只能使用方式三​

    class Worker(Person):
    def __init__(self, name, age, gender, type):
    super(Worker, self).__init__(name, age, gender)
    self.type = type

    def work(self):
    print("working")


    w = Worker("工作者", 10, "男", "行政")
    print(w.type) # 行政
    print(w.name, w.age, w.gender) # 工作者 10 男
    2. 继承中的 ​​__slots__​​ 属性
    • ​​__slots__​​ 定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用
    class Aniaml(object):
    __slots__ = ("name", "age")


    class Cat(Aniaml):
    pass


    c = Cat()
    c.name = "tom"
    c.age = 6
    c.num = 8
    print(c.name, c.age, c.num) # tom 6 8
    • 如果子类本身也有​​__slots__​​​ 属性,子类的属性就是自身的​​__slots__​​​ 加上父类的​​__slots__​​
    class Aniaml(object):
    __slots__ = ("name", "age")


    class Cat(Aniaml):
    __slots__ = ("num",)


    c = Cat()
    c.name = "tom"
    c.age = 6
    c.num = 8
    # c.weight = 80 # 报错
    print(c.name, c.age, c.num) # tom 6 8
    3. 继承中的类属性
    class MyClass1(object):
    x = 10


    class SubClass1(MyClass1):
    pass


    class SubClass2(MyClass1):
    pass


    print(MyClass1.x, SubClass1.x, SubClass2.x) # 10 10 10

    # 通过子类修改继承自父类的类属性,只会改变该子类继承的类属性
    SubClass1.x = 20
    print(MyClass1.x, SubClass1.x, SubClass2.x) # 10 20 10

    # 通过父类修改类属性,子类继承的类属性也改变
    MyClass1.x = 30
    print(MyClass1.x, SubClass1.x, SubClass2.x) # 30 20 30

    # 子类先修改继承的类属性,父类再修改类属性,将不会再对已经修改过的子类类属性做出改变
    SubClass2.x = 50
    MyClass1.x = 40
    print(MyClass1.x, SubClass1.x, SubClass2.x) # 40 20 50
    4. 继承的特点
  • 子类对象可以直接访问父类中未被私有化的属性和函数
  • 父类的​​__slots__​​ 属性对子类不起作用
  • 父类对象不能访问子类中特有的属性和函数
  • 5. 继承的优缺点

    继承的优点:

    • 可以简化代码,减少冗余
    • 提高代码的可维护性
    • 是多态的前提

    继承的缺点:

    • 在继承关系中,类与类之间的耦合性相对较高【如果修改父类,所有的子类都可能会发生改变】

    多继承

    子类可以拥有多个父类,并且具有所有父类的属性和方法。

    1. 基本使用
    class Father1(object):
    def __init__(self, num1):
    self.num1 = num1

    def func1(self):
    print("func1~~~111")

    def show(self):
    print("show~~~1111")


    class Father2(object):
    def __init__(self, n1, n2):
    self.n1 = n1
    self.n2 = n2

    def func2(self):
    print("func2~~~222")

    def show(self):
    print("show~~~2222")

    如果一个子类继承了多个父类,在没有书写构造函数的前提下,创建子类对象,默认调用的父类列表中的第一个父类中的构造函数。

    class Child(Father2, Father1):
    pass


    c1 = Child(2, 3)
    c2 = Child(1) # 报错

    如果需要继承父类中的属性,则需要调用父类的构造函数。

    class Child(Father1, Father2):
    def __init__(self, num1, n1, n2, a):
    Father1.__init__(self, num1)
    Father2.__init__(self, n1, n2)
    self.a = a


    c = Child(3, 4, 6, 8)
    print(c.a) # 8
    print(c.num1, c.n1, c.n2) # 3 4 6
    c.func1() # func1~~~111
    c.func2() # func2~~~222

    如果多个父类中出现了重名的函数,则子类对象调用时,调用的是父类列表中的第一个父类中的函数。

    class Child(Father1, Father2):
    def __init__(self, num1, n1, n2, a):
    Father1.__init__(self, num1)
    Father2.__init__(self, n1, n2)
    self.a = a


    c = Child(3, 4, 6, 8)
    c.show() # show~~~1111
    2. 继承的 ​​__mro__​​ 属性

    Python中针对​类​提供了一个内置属性 ​​__mro__​​ 可以用来查看方法的搜索顺序。

    ​​mro​​​ 是 ​​method resolution order​​ 的简称,主要用于在多继承时判断方法属性的调用顺序。

    • 在调用方法时,按照​​__mro__​​ 的输出结果从左至右的顺序查找。
    • 如果再当前类中找到方法,就直接执行,不再向下搜索。
    • 如果没有找到,就顺序查找下一个类中是否有对应的方法,如果找到,就直接执行,不再向下搜索。
    • 如果找到了最后一个类,依然没有找到方法,程序就会报错。
    class A(object):
    def show(self):
    print("aaaa")


    class B(A):
    def show(self):
    print("bbbb")


    class C(A):
    def show(self):
    print("cccc")


    class D(B, C):
    def show(self):
    print("dddd")


    print(D.__mro__)
    # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    d = D()
    d.show() # ddd
    3. 多继承中 super 本质(继承树问题)

    不是直接查找父类,而是根据调用节点的​广度优先​顺序执行的。

    举个例子,创建A、B、C、D、E类,E类继承B、C、D类,B类继承A类,C类继承A类,D类继承A类,在每个方法中都调用 ​​super().show()​​ 方法,查看执行顺序。

    class A(object):
    def show(self):
    print("aaaa")


    class B(A):
    def show(self):
    print("enter~~~~bbb")
    super().show()
    print("退出~~~~~bbb")


    class C(A):
    def show(self):
    print("enter~~~~ccc")
    super().show()
    print("退出~~~~~ccc")


    class D(A):
    def show(self):
    print("enter~~~~ddd")
    super().show()
    print("退出~~~~~ddd")


    class E(B, C, D):
    def show(self):
    print("enter~~~~eee")
    super().show()
    print("退出~~~~~eee")


    print(E.mro())
    # [<class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
    e = E()
    e.show()
    """
    enter~~~~eee
    enter~~~~bbb
    enter~~~~ccc
    enter~~~~ddd
    aaaa
    退出~~~~~ddd
    退出~~~~~ccc
    退出~~~~~bbb
    退出~~~~~eee

    """

    另外一个例子,在C类中没有调用 ​​super().show()​​ 方法。

    class A(object):
    def show(self):
    print("aaaa")


    class B(A):
    def show(self):
    print("enter~~~~bbb")
    super().show()
    print("退出~~~~~bbb")


    class C(A):
    def show(self):
    print("enter~~~~ccc")
    print("退出~~~~~ccc")


    class D(A):
    def show(self):
    print("enter~~~~ddd")
    super().show()
    print("退出~~~~~ddd")


    class E(B, C, D):
    def show(self):
    print("enter~~~~eee")
    super().show()
    print("退出~~~~~eee")


    print(E.mro())
    # [<class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
    e = E()
    e.show()
    """
    enter~~~~eee
    enter~~~~bbb
    enter~~~~ccc
    退出~~~~~ccc
    退出~~~~~bbb
    退出~~~~~eee
    """

    函数重写

    1. 自定义函数
    • 父类中函数的实现不满足子类的需求
    class Person(object):
    def func1(self):
    print("父类~~~func1")

    def show(self):
    print("父类~~~show")


    class Student(Person):
    # 建议:重写的时候,子类中的函数的声明部分尽量和父类中的函数保持一致
    def func1(self):
    print("子类~~~~func1")
    return 100


    stu1 = Student()
    stu1.func1() # 子类~~~~func1
    • 父类中函数的实现 部分 满足子类的需求
    class Person(object):
    def func1(self):
    print("父类~~~func1")

    def show(self):
    print("父类~~~show")


    class Student(Person):
    def func1(self):
    super().func1()
    print("子类~~~~func1")
    return 100


    stu1 = Student()
    stu1.func1()
    """
    父类~~~func1
    子类~~~~func1
    """
    2. 系统函数
  • ​​__str__​​​ 和​​__repr__​​
  • 打印对象时,会调用对象的 ​​__str__​​ 方法,默认会打印类名和对象的地址名;

    ​​__repr__​​​ 方法 和 ​​__str__​​​ 方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写 ​​__str__​​​ 方法,它会自动来查找 ​​__repr__​​​ 方法。如果这两个方法都没有,会直接打印这个对象的内存地址。如果两个方法都写了,选择 ​​__str__​​ 方法。

    class Check1(object):
    def __init__(self, name, age, score):
    self.name = name
    self.age = age
    self.score = score

    def __str__(self):
    return f"name:{self.name},age:{self.age},score:{self.score}"

    __repr__ = __str__


    # 如果只是打印一个对象,则重写__str__即可
    c1 = Check1("aaa", 10, 199)
    c2 = Check1("bbb", 20, 88)

    # 如果将对象添加到序列中,为了能看到对象的值,则重写__str__和__repr__
    list1 = [c1, c2]
    print(list1)
    for c in list1:
    print(c)
  • ​​__add__​​
  • print("hello" + "Python")
    print("hello".__add__("Python"))class Book(object):
    def __init__(self, num):
    self.num = num

    # 重写
    def __add__(self, other):
    # 参与相加的两个对象:self 和 other
    # Book + Book = Book
    return Book(self.num + other.num)

    # 重写__str__,保证最后返回的结果
    def __str__(self):
    return f"{self.num}"


    b1 = Book(100)
    b2 = Book(400)
    b3 = b1 + b2
    print(b3) # 500
    print(b1.__add__(b2)) # 500
  • ​​__eq__​​
  • ​​__eq__​​ 方法如果不重写,默认比较依然是内存地址

    class Student(object):
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def __eq__(self, other):
    return self.name == other.name and self.age == other.age


    s1 = Student('lucy', 18)
    s2 = Student('lucy', 18)
    s3 = Student('lucy', 20)

    # s1 == s2本质是调用 p1.__eq__(p2),获取这个方法的返回结果
    print(s1 == s2) # True
    print(s1 == s3) # False
  • 比较运算符相关魔法方法
  • class Student:
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def __eq__(self, other): # 等于
    return self.name == other.name and self.age == other.age

    def __ne__(self, other): # 不等于
    return self.age != other.age

    def __lt__(self, other): # 小于
    return self.age < other.age
    # return self.age < other

    def __gt__(self, other): # 大于
    return self.age > other.age

    def __le__(self, other): # 小于等于
    return self.age <= other.age

    def __ge__(self, other): # 大于等于
    return self.age >= other.age


    s1 = Student('zhangsan', 18)
    s2 = Student('zhangsan', 18)
    s3 = Student('lisi', 20)
    print(s1 == s2) # True
    print(s1 != s2) # False
    print(s1 < s2) # False
    # print(s1 < 6) # False
    print(s1 > s2) # False
    print(s1 <= s2) # True
    print(s1 >= s2) # True
  • 算术运算符相关魔法方法
  • class Student:
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def __add__(self, other):
    return self.age + other

    def __sub__(self, other):
    return self.age - other

    def __mul__(self, other):
    return self.age * other

    def __truediv__(self, other):
    return self.age / other

    def __mod__(self, other):
    return self.age % other

    def __pow__(self, power, modulo=None):
    return self.age ** power


    s = Student('zhangsan', 18)
    print(s + 1) # 19
    print(s - 2) # 16
    print(s * 2) # 36
    print(s / 5) # 3.6
    print(s % 5) # 3
    print(s ** 2) # 324
  • 类型转换相关魔法方法
  • class Student:
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def __int__(self):
    return self.age

    def __float__(self):
    return self.age * 1.0

    def __str__(self):
    return self.name

    def __bool__(self):
    return self.age > 18


    s = Student('zhangsan', 18)
    print(int(s)) # 18
    print(float(s)) # 18.0
    print(str(s)) # zhangsan
    print(bool(s)) # False

    七、多态

    不同的子类调用相同的父类方法,产生不同的执行结果,可以增加代码的外部灵活度。多态是​以继承和重写父类方法​为前提的,它是一种调用方法的技巧,不会影响到类的内部设计。

    场景

    • 提供三个类:缉毒犬、军犬、人
    • 缉毒犬(追查毒品),军犬(攻击敌人),人(让小狗干活)
    • 设计类来完成功能

    实现

    class ArmyDog(object):

    def bite_enemy(self):
    print('追击敌人')


    class DrugDog(object):

    def track_drug(self):
    print('追查毒品')


    class Person(object):

    def work_with_army(self, dog):
    dog.bite_enemy()

    def work_with_drug(self, dog):
    dog.track_drug()


    ad = ArmyDog()
    dd = DrugDog()

    p = Person()
    p.work_with_army(ad)
    p.work_with_drug(dd)

    思考:这段代码设是否有问题?

    新增需求:多了一个犬种,需要在Person类里新建一个方法,让这个方法操作新的狗。

    class XiaoTianDog(object):

    def eat_moon(self):
    print('哮天犬把月亮吃了')


    class Person(object):

    def work_with_xiaotian(self, dog): # 添加方法
    dog.eat_moon()

    Person 类总是不断的添加新的功能,每次都需要改动Person类的源码,程序的扩展性太差了!

    最好是提供一个父类 Dog,具备 work 的功能,其他小狗继承它,这样只要是小狗类,则行为被统一起来了,我们人类完全可以保证,只要是小狗的子类,找它干活肯定不会有问题。这样人只要一个方法就能逗任意种类的狗玩,哪怕是添加新的狗,人的类都不需要修改。

    最终实现

    class Dog(object):

    def work(self): # 父类提供统一的方法,哪怕是空方法
    pass


    class ArmyDog(Dog): # 继承 Dog

    def work(self): # 子类重写方法,并且处理自己的行为
    print('追击敌人')


    class DrugDog(Dog):

    def work(self):
    print('追查毒品')


    class XiaoTianDog(Dog):

    def work(self):
    print('哮天犬把月亮吃了')


    class Person(object):

    def work_with_dog(self, dog):
    dog.work() # 使用小狗可以根据对象的不同而产生不同的运行效果, 保障了代码的稳定性


    # 子类对象可以当作父类来使用
    dog = Dog()
    ad = ArmyDog()
    dd = DrugDog()
    xtd = XiaoTianDog()

    p = Person()

    # 同一个方法,只要是 Dog 的子类就可以传递,提供了代码的灵活性
    # 并且传递不同对象,最终 work_with_dog 产生了不同的执行效果
    p.work_with_dog(dog)
    p.work_with_dog(ad)
    p.work_with_dog(dd)
    p.work_with_dog(xtd)

    Person 类中只需要调用 Dog 对象 ​​work()​​ 方法,而不关心具体是 什么狗

    ​​work()​​ 方法是在 Dog 父类中定义的,子类重写并处理不同方式的实现

    在程序执行时,传入不同的 Dog 对象作为实参,就会产生不同的执行效果

    多态总结

    • 定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
    • 好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
    • 实现步骤:
    • 定义父类,并提供公共方法
    • 定义子类,并重写父类方法
    • 传递子类对象给调用者,可以看到不同子类执行效果不同

    八、其他

    对象的内置属性

    1. ​​__slots__​​

    限制对象属性的动态绑定。

    2. ​​__doc__​​

    表示类的描述信息,获取类中的文档注释(多行注释)。

    class Check(object):
    """
    功能:实现校验的功能
    参数
    返回值
    """

    def show(self):
    pass


    print(Check.__doc__)
    c = Check()
    print(c.__doc__)
    3. ​​__dict__​​

    获取类或者对象的信息【属性和方法】,返回字典。

    class MyClass(object):
    num = 10

    def __init__(self, m):
    self.m = m

    def show(self):
    pass

    @classmethod
    def func(cls):
    pass

    @staticmethod
    def test():
    pass


    # 类名:类属性,构造函数,实例函数,类函数,静态函数
    print(MyClass.__dict__)
    m = MyClass(5)

    # 对象:实例属性
    print(m.__dict__)

    总结:

    • 内置的数据类型没有​​__dict__​​属性

    • 每个类有自己的​​__dict__​​​属性,就算存着继承关系,父类的​​__dict__​​​ 并不会影响子类的​​__dict__​​

    • 对象也有自己的​​__dict__​​属性,存储实例属性信息

    4. ​​__module__​​

    获取当前操作的对象在哪个模块。

    如果被操作的对象在当前模块,则获取的结果为​​__main__​​​ ,如果被操作的对象在其他模块,则获取的结果为 ​​模块名​​ 。

    class MyClass(object):
    pass


    m = MyClass()
    print(m.__module__) # __main__from random import randint

    print(randint.__module__) # random
    5. ​​__class__​​

    类似于​​type(xxx)​​ ,返回当前对象的类型。

    class MyClass(object):
    pass


    m = MyClass()
    print(m.__class__) # <class '__main__.MyClass'>
    print(type(m)) # <class '__main__.MyClass'>print('abc'.__class__) # <class 'str'>
    print(type('abc')) # <class 'str'>

    对象的内置函数

    1. ​​id()​​

    获取一个对象在内存中的地址

    a = 99
    print(id(a))
    print(type(id(a)))

    # 比较两个对象的地址是否相同
    a = 19
    b = 10
    print(id(a) == id(b))
    print(a is b)
    2. ​​type()​​
    • 获取对象的类型
    print(type("abc")) # <class 'str'>
    • type的返回值可以直接比较是否相等
    print(type(20) == type(40)) # True
    print(type(20) == int) # True
    print(type(type(10))) # <class 'type'>class Person(object):
    pass


    p = Person()
    print(type(p)) # <class '__main__.Person'>
    print(type(Person)) # <class 'type'>
    • 借助​​types​​ 模块,可以判断函数的类型
    import types

    print(type(lambda x: x) == types.LambdaType)
    print(type((x for x in range(10))) == types.GeneratorType)
    print(type(abs) == types.BuiltinFunctionType)
    3. ​​isinstance()​​

    判断一个实例对象是否是由某一个类(或者它的子类)实例化创建出来的。

    class Person(object):
    def __init__(self, name, age):
    self.name = name
    self.age = age


    class Student(Person):
    def __init__(self, name, age, score):
    super(Student, self).__init__(name, age)
    self.score = score


    p = Person('tony', 18)
    s = Student('jack', 20, 90)

    print(isinstance(p, Person)) # True.对象p是由Person类创建出来的
    print(isinstance(s, Person)) # True.对象s是有Person类的子类创建出来的print(isinstance(18, int))
    print(isinstance([1, 2, 3], (list, tuple, dict)))
    print(isinstance(33, (str, int)))
    4. ​​dir()​​

    查看对象内的所有的属性和方法。

    # 获得当前模块的属性
    print(dir())print(dir("abcd"))
    5. ​​issubclass(子类, 父类)​​

    判断两个类之间的继承关系。

    class Person(object):
    def __init__(self, name, age):
    self.name = name
    self.age = age


    class Student(Person):
    def __init__(self, name, age, score):
    super(Student, self).__init__(name, age)
    self.score = score


    class Dog(object):
    def __init__(self, name, color):
    self.name = name
    self.color = color


    print(issubclass(Student, Person)) # True
    print(issubclass(Dog, Person)) # False

    类方法

    • 形参列表的第一个参数为​​cls​​ ,表示当前类;

    • 使用 ​​@classmethod​​ 装饰器来装饰。

    ​使用场景​

    • 当方法中​需要使用类对象​(如访问私有类属性等)时,定义类方法
    • 类方法一般和类属性配合使用
    class Person(object):
    # 类属性
    __num = 3

    # 构造函数
    def __init__(self, name, age):
    # 实例属性
    self.name = name
    self.age = age

    # 实例函数:形参列表的第一个参数必须为self,表示当前对象
    def show(self):
    print("show", self)
    print("showing")

    @classmethod
    def func1(cls):
    # cls就相当于是Person,所以可以通过cls创建对象
    c = cls("jack", 18)
    # 在类方法中调用 实例函数,必须先通过cls创建对象,然后再调用
    c.show()
    return cls.__num


    p = Person("tom", 5)

    # 类函数可以通过类名或者对象调用
    print(p.func1())
    print(Person.func1())
    """
    show <__main__.Person object at 0x000001D1865BB308>
    showing
    3
    """

    静态方法

    • 需要通过装饰器​​@staticmethod​​ 来进行修饰,​静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)​。
    • 静态方法 也能够通过​实例对象​和​类对象​去访问。

    ​使用场景​

    • 当方法中​既不需要使用实例对象​(如实例对象,实例属性),​也不需要使用类对象​(如类属性、类方法、创建实例等)时,定义静态方法
    • ​取消不需要的参数传递​,有利于​减少不必要的内存占用和性能消耗​
    class Dog(object):
    type = "狗"

    def __init__(self):
    name = None

    # 静态方法
    @staticmethod
    def introduce(): # 静态方法不会自动传递实例对象和类对象
    print("工作赚钱了养只金毛")


    dog1 = Dog()
    Dog.introduce() # 可以用 实例对象 来调用 静态方法
    dog1.introduce() # 可以用 类对象 来调用 静态方法

    ​注意点​

    类中定义了同名的方法时,调用方法会执行最后定义的方法。

    class Dog:

    def demo_method(self):
    print("对象方法")

    @classmethod
    def demo_method(cls):
    print("类方法")

    @staticmethod
    def demo_method(): # 被最后定义
    print("静态方法")


    dog1 = Dog()
    Dog.demo_method() # 结果: 静态方法
    dog1.demo_method() # 结果: 静态方法

    九、单例设计模式

    概念

    程序运行过程中,确保某一个类只有一个实例【对象】,不管在哪个模块获取这个类的对象,获取到的都是同一个对象。该类有一个静态方法,向整个工程提供这个实例,例如:一个国家只有一个主席,不管他在哪里。

    单例设计模式的核心:一个类有且仅有一个实例,并且这个实例需要应用于整个程序中,该类被称为单例类。

    应用场景

    应用程序中描述当前使用用户对应的类,当前用户对于该应用程序的操作而言是唯一的,所以一般将该对象设计为单例。

    实际应用:数据库连接池操作,应用程序中多处地方连接到数据库,连接数据库时的连接池只需一个就行,没有必要在每个地方都创建一个新的连接池,这种也是浪费资源 ,解决方案也是单例。

    实现

    1. 模块
    class Person(object):
    def __init__(self, name):
    self.name = name

    def dance(self):
    print(self.name + "dancing")


    p = Person("小彩旗")
    print(id(p))from Day15.singleton.person import p

    # 依据:Python中的模块本身是一种单例,因为只有当第一次import的时候才会去加载源文件【.pyc】,
    print(p)
    print(id(p))
    2. ​​__new__​​
    class Person(object):
    # 定义类属性,用于存储当前类可以创建的唯一的实例
    __instance = None

    # 只要执行创建对象的语法,都会调用__new__,也都会将__new__的返回值传递给__init__的self,进行属性的赋值
    def __new__(cls, *args, **kwargs):
    # 思路:判断__instance 值是否为None,如果为None,则赋值【当前实例】,如果不为None,则直接返回
    if not cls.__instance:
    cls.__instance = super(Person, cls).__new__(cls)
    return cls.__instance

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


    p1 = Person("jack", 10) # 0x67345674 第一次肯定会创建
    print(p1)
    p2 = Person("aaa", 6) # 0x67345674 第二次则表示获取,但是会将属性值重新赋值
    print(p2)
    print(id(p1) == id(p2)) # True
    print(p1 is p2) # True

    print(p1.name, p2.name) # aaa aaa
    3. 装饰器

    ​装饰器装饰函数​

    def outter(func):
    print("ouuter~~~~")

    def inner():
    print("inner~~~")
    func()

    return inner


    @outter
    def test():
    print("test")


    test()

    ​装饰器装饰类​

    def outter(cls):
    print("outter~~~222")

    def inner():
    print("inner~~~~222")

    return cls()

    return inner


    @outter
    class Check(object):
    pass

    # 只要上述代码,会打印outter~~~222
    c = Check()
    print(c)
    c1 = Check()
    print(c1)

    """
    outter~~~222
    inner~~~~222
    <__main__.Check object at 0x000002422E4E8B88>
    inner~~~~222
    <__main__.Check object at 0x000002422E4E5788>
    """def outter(cls):
    print("outter~~~222")

    def inner(*args, **kwargs):
    print("inner~~~~222")

    return cls(*args, **kwargs)

    return inner


    @outter
    class Check(object):
    def __init__(self, name, age):
    self.name = name
    self.age = age


    c = Check("jack", 10)
    print(c)
    print(c.name, c.age)
    """
    outter~~~222
    inner~~~~222
    <__main__.Check object at 0x0000020A220CB2C8>
    jack 10
    """

    ​实现单例​

    def singleton(cls):
    # 定义一个变量,用于存储被装饰的类唯一的实例
    instance = None

    # 建议:函数命名为getinstance/currentinstance/defaultinstance
    def getinstance(*args, **kwargs):
    # 思路:判断instance值是否为None,如果为None,则赋值【当前实例】,如果不为None,则直接返回
    # nonlocal instance声明局部作用域中的instance不是定义了新的变量,而是来自于函数作用域的instance
    nonlocal instance
    if not instance:
    # cls()会自动调用原类的构造函数【__init__】
    instance = cls(*args, **kwargs)
    # 返回被装饰类唯一的实例
    return instance

    return getinstance


    @singleton
    class Person(object):
    def __init__(self, name, age, score):
    self.name = name
    self.age = age
    self.score = score


    p1 = Person("zhangsan", 10, 99)
    p2 = Person("lisi", 18, 100)
    print(p1 is p2) # True
    print(id(p1) == id(p2)) # True

    print(p1.name, p2.name) # zhangsan zhangsandef singleton(cls):
    instance_dict = {}

    def getinstance(*args, **kwargs):
    # 思路:判断instance_dict是否为空,如果为空,以被装饰的类作为key,唯一的实例作为value
    # 如果不为空,则直接返回value
    if not instance_dict:
    instance_dict[cls] = cls(*args, **kwargs)
    return instance_dict[cls]

    return getinstance


    @singleton
    class Person(object):
    def __init__(self, name, age, score):
    self.name = name
    self.age = age
    self.score = score


    p1 = Person("zhangsan", 10, 99)
    p2 = Person("lisi", 18, 100)
    print(p1 is p2) # True
    print(id(p1) == id(p2)) # True

    print(p1.name, p2.name) # zhangsan zhangsan

    ​类函数​

    # 思路:在当前类中创建一个实例,在类的外面只需要获取
    class Person(object):
    # 定义一个类私有属性,保存当前类唯一的实例
    __instance = None

    @classmethod
    def getinstance(cls, *args, **keargs):
    # 创建对象
    if not cls.__instance:
    cls.__instance = cls(*args, **keargs)
    return cls.__instance

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


    p1 = Person.getinstance("zhangsan", 10)
    print(p1) # <__main__.Person object at 0x00000287C9535A08>
    p2 = Person.getinstance("lisi", 20)
    print(p2) # <__main__.Person object at 0x00000287C9535A08>

    print(p1 is p2) # True
    print(id(p1) == id(p2)) # True

    print(p1.name, p2.name) # zhangsan zhangsan

    十、枚举类

    使用枚举类的好处

    枚举类可以方便地表示星期,月份等常数类型,如果你不用枚举类,那么你只能用数字或者字符串。如果你使用数字,用1-7来表示星期数,但一个数字在程序中不仅可以表示星期数,可能还有其他许多含义,这样你在写程序时就必须时刻记住这些数字的含义,这降低了程序的可读性,也导致容易出错。而当你使用字符串时,虽然没有明显的缺点,但在内存中字符串所占内存要比数字多,这就降低了程序的效率。

    枚举类正好弥补了这两方面的缺点,你可以在代码中使用枚举类,但在内存中存放时使用的是数字,既提高了可读性,又提高了程序效率。更重要的是,Python中的枚举类型是不可变类型,又可以进行迭代,这就意味着可以随时使用枚举类型而不用担心改变了枚举类型的值。

    创建枚举类

    枚举类型可以通过继承Enum类来实现,注意Enum类是在enum模块中的。

    from enum import Enum


    class Week(Enum):
    # 枚举成员
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 0

    查看枚举类型

    # 枚举类型
    print(Week.MONDAY, type(Week.MONDAY)) # Week.MONDAY <enum 'Week'>
    print(Week['MONDAY']) # Week.MONDAY

    # 枚举名称 key
    print(Week.MONDAY.name) # MONDAY

    # 枚举值 value
    print(Week.MONDAY.value) # 1

    # 通过value得到枚举类型
    print(Week(0)) # Week.SUNDAY

    # 遍历查询枚举成员的值
    for w in Week:
    print(w.value)

    重复的枚举类型

    当存在枚举成员有重复时,则后面的枚举成员相当于第一个枚举成员的别名,而且在实际使用中,就是使用的第一次出现的枚举成员。

    from enum import Enum


    class Week(Enum):
    # 枚举成员
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 0
    ALIAS_FOR_SUNDAY = 0

    上述代码中,​​ALIAS_FOR_SUNDAY​​​ 就是 ​​SUNDAY​​ 的别名,就比如“星期日”和“星期天”都可以表示星期七一样,当遇到这种情况我们也可以这样用。

    ​如果尝试遍历枚举类型,则后面重复的不会被打印出来​。但是,如果想要获取别名,我们可以使用属性​​__members__​​,它是一个OrderedDict,包括所有定义的枚举名称,包括别名。

    for name, member in Week.__members__.items():
    print(name, member)

    确保枚举类型的唯一

    枚举类型默认可以对相同的值使用别名,但有时我们需要确保枚举类型不能重复,我们也有办法使每个枚举值只出现一次。我们可以引入装饰器​​@unique​​,它会遍历枚举成员,如果发现有重复就会立即抛出 ValueError。

    from enum import Enum, unique


    @unique
    class Week(Enum):
    # 枚举成员
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 0
    # ALIAS_FOR_SUNDAY = 0 # ValueError: duplicate values found in <enum 'Week'>: ALIAS_FOR_SUNDAY -> SUNDAY

    IntEnum

    限制枚举成员必须是整数类型。

    from enum import Enum, unique, IntEnum


    class Flag(IntEnum):
    A = 3
    B = 19
    C = 10
    # D = "abc" # ValueError: invalid literal for int() with base 10: 'abc'

    枚举类的其他特性

    最有趣的也是最重要的是枚举类型是使用单例模式实现的。在创建枚举类的时候,Python就在内存中为我们创建了枚举类的对象,因此我们不必实例化枚举类。并且由于枚举类的​​__new__​​方法,将会保证内存中只会存在一个枚举类的实例。

    十一、接口类

    继承有两种用途:

    一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)

    二:声明某个子类兼容于某基类,定义一个接口类​​Interface​​,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能。

    实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合,继承的第二种用途非常重要。它又叫​接口继承​。

    # 接口类,将多个类中的共同的需求做出了一个抽象
    class Payment(object):
    # 规定了一个兼容的接口
    def pay(self):
    pass


    class WechatPay(Payment):
    def pay(self, money):
    print("微信支付")


    class AliPay(Payment):
    def pay(self, money):
    print("支付宝支付")


    class ApplePay(Payment):
    def pay(self, money):
    print("苹果支付")


    def func(obj, money):
    obj.pay(money)


    # 创建对象
    w = WechatPay()
    a = AliPay()
    a1 = ApplePay()

    func(w, 1000)
    func(a, 1000)
    func(a1, 1000)

    十二、抽象类

    由于python 没有抽象类、接口的概念,所以要实现这种功能得借助于 ​​abc​​ 这个类库。

    抽象类是一个特殊的类,它的特殊之处在于​只能被继承,不能被实例化​。

    import abc # abstract class


    # 1.定义一个抽象类,并在其中定义抽象方法
    class Check1(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
    pass

    @abc.abstractmethod
    def write(self):
    pass


    # 注意1:抽象类本身不能被实例化
    # c1 = Check1()

    # 注意2:抽象类存在的意义就是为了被继承
    # 注意3:在继承了抽象类的子类中必须实现抽象类中的方法
    class SubClass1(Check1):
    def read(self):
    print("读取")

    def write(self):
    print("写入")


    s1 = SubClass1()
    s1.read()
    s1.write()



    网友评论