Python面向对象学习笔记

Python面向对象编程

记录人: 李思成

时间: 2024/05/01至2024/05/23

课程来源: B站Python面向对象

1.面向对象编程概述

官方概述

程序是指令的集合,运行程序时,程序中的语句会变成一条或多条指令,然后由CPU(中央处理器)去执行。为了简化程序的设计,引入了函数,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些代码的时候调用函数即可。如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数进一步拆分为多个子函数来降低系统的复杂性。
诞生于上世纪70年代的Smalltalk语言让软件开发者看到了希望,因为它引入了一种新的编程范式叫"面向对象编程"。在面向对象编程的世界里,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为对象,对象可以接收消息,解决问题的方法就是创建对象并向对象发出各种各样的消息;通过消息传递,程序中的多个对象可以协同工作,这样就能构造出复杂的系统并解决现实中的问题。

通俗概述

面向对象编程:把一组数据和处理数据的方法组成"对象",把行为相同的"对象"归纳为"类",通过"封装"隐藏"对象"的内部细节,通过"继承"实现"类"的特化和泛化,通过"多态"实现基于对象类型的动态分派。类是对象的蓝图和模板,对象是类的实例,是可以接受消息的实体。在面向对象编程的世界中,“一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类”。对象的属性是对象的静态特征,对象的行为是对象的动态特征。按照上面的说法,如果我们把拥有共同特征的对象的属性和行为都抽取出来,就可以定义出一个类。

2.面向过程&面向对象

面向对象和面向过程都是解决问题的一种思想,面向对象本身是对象向过程的封装。

1. 面向过程编程:按照步骤划分,把一个任务,分解成具体的每一个步骤。
2. 面向对象编程:按照对象功能进行划分,找到对象,确定对象属性和行为。

如何从面向过程过度到面向对象?

1. 列举出一个任务的具体实现步骤
2. 试图分离这些实现步骤中的功能代码块
3. 将这些功能代码块,划分到某一个对象中
4. 根据这个对象以及对应的行为,抽象出对应的类–设计类

3.类

3.1类的概念

对于某个具体对象特征的抽象。

例如:张三的属性(年龄:18,身高:180,体重:250),张三的行为(吃喝嫖赌)
抽象出来的类:不良青年:属性(),行为()

3.2类的作用

根据抽象的类,生产出具体的对象。

在这里插入图片描述

3.3类的组成

类的名称、属性、方法。类的属性和方法都是抽象的概念,没有具体的值。类在产生对象后,对象才有具体的属性值和方法实现

3.4类与对象的关系

类可以实例化一个对象,对象可以抽象出来一个类。

  • 每个人都有自己的姓名,年龄,性别,身高,这些可以看作是每个人(实例对象)共有的属性;每个人都有说话,吃饭,睡觉这些动作,动作就是方法,也就是函数。拥有这些共同的属性和方法的个体都可以被划分到人类中。人类是一个类对象,每个具体的人是一个实例对象。
  • Python中万物皆对象,对象分为类对象和实例对象。类对象在执行class语句时创建。使用类名称可以调用类对像,类对象也称作类实例。调用类对象可以创建类的实例对象。类对象只能有一个,实例对象可以创建多个。类对象和类的实例对象分别拥有自己的命名空间,它们在各自的命名空间内使用对象的属性和方法。

3.5定义类

Python中可以根据需求来自定义类,class语句作为定义类的关键字,类中使用赋值语句定义类属性,使用def关键字定义类方法。在使用类时,需要先定义类,再创建类的实例,通过类的实例就可以访问类中的属性和方法。

class 类名:
	赋值语句
	赋值语句
	......
	def 定义函数
	def 定义函数
    ......

类是一种自定义类型,使用class关键字作为定义类的开头,后面加类的名称,类的名称与定义函数的名称有一定区别,类名首字母需要大写,建议使用大驼峰命名方式(如果需要使用多个单词表示类名时每个单词首字母大写),使用赋值语句创建类变量(类属性),使用def关键字定义类的函数(类方法),示例如下:

class Money:
    # 定义属性
    sex = "男人"

    # 定义初始化方法
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height
    
    #类方法
    def walk(self):
   		print('直立行走')
        
    def speak(self):
        print("%s 说: 我 %d 岁, 身高 %d 厘米" % (self.name, self.age, self.height))

类中定义属性和方法无需分先后顺序,上述示例定义了Money类,其中sex为类变量(类属性),walk()speak()为类方法,__ init __()为初始化方法或者称为构造方法。

3.6据类创建对象

class Money:
    pass

#根据这个类,创建(实例化)一个对象
one = Money()
print(one)
#输出:<__main__.Money object at 0x000002894319C0A0>

3.7创建对象时的底层运行逻辑

黄色框为类属性。one指向了Money实例化的对象的地址(实例化对象的时候也开辟了一块内存)。Money类产生的对象里有一个属性值__class__,是类的地址。所以在操作one变量的时候会先找到Money类产生的对象,然后通过__class__关联到Money这个类。当我们操作one这个变量时,其实操作的是one这个变量所引用的对象。

在这里插入图片描述

3.8类知识点扩展

这个Money不仅是一个类名(类名不可被修改),还是一个变量名称(可以被修改)。

class Money:
    pass

#给Money赋值666
Money = 666

print(Money) #输出:666

#此时Money不再是一个类,而是int
print(type(Money))#输出:<class 'int'>
#因此得证

在Python中,类本身也是一个对象。因此,我们可以将一个类赋给一个变量,从而可以像操作其他对象一样操作这个类。

class Money:
    # 定义属性
    sex = "男人"

    # 定义初始化方法
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height
    
    #实例方法
    def walk(self):
   		print('直立行走')

print(Money.__name__) #输出类名:Money

#将Money类赋值给变量xxx。这样,xxx就指向了Money类
xxx = Money
print(xxx.__name__) #输出类名:Money

#现在我们就可以使用xxx来实例化对象,并调用类中的方法
person = xxx("lili",18,182)
person.walk()

#值得注意的是
#执行该语句输出:<class 'type'>
print(type(xxx))

#执行该语句输出:<class '__main__.Money'>
print(type(person))

在上面的代码中,我们实例化了一个Money类的对象,并调用了Money类的walk方法。由于我们将Money类赋给了xxx变量,因此可以通过xxx来实例化对象person

3.9属性与变量的区别

概念

  1. 变量:可以改变的量值

  2. 属性:属于某个对象的特征

访问权限

  1. 变量:根据不同的位置,存在不同的访问权限(全局变量,局部变量)

  2. 属性:只能通过对象来进行访问。所以访问时,要先找到对象。对象也是通过变量名来引用;而既然是变量,也有对应的访问权限。

4.对象属性

4.1添加对象属性

方法一:直接通过对象,动态添加 语法: 对象.属性 = 值

class Person:
    pass

#根据类,创建一个对象
p = Person()

#给p对象增加一些属性
p.age = 18
p.height = 180

#验证是否有添加成功
print(p.age)
print(p.height)
print(p.__dict__)  
#__dict__这个属性是python自动加的,功能是可以查看对象的所有属性和值,会返回一个字典

方法二:通过类的初始化方法(构造方法 ) __init__方法

class People:
    # 定义初始化方法
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

4.2修改对象属性

#定义一个类
class Person:
    pass

#根据类,创建一个对象
p = Person()

#添加对象属性
p.pets = ["小狗","小猫"]
print(p.pets,id(p.pets)) #['小狗', '小猫'] 2508693300352

#函数id()用于返回对象的内存地址,即其身份。
#方式一:不会开辟新的空间。可以观察到,添加后id()没有改变。
p.pets.append("小黑") #p.pets是访问操作,后采用append进行追加。
print(p.pets,id(p.pets)) #['小狗', '小猫', '小黑'] 2508693300352

#方法2:开辟了新的空间。可以观察到,添加后id()发生了改变。
p.pets = ["小狗","小猫","小黑"]
print(p.pets,id(p.pets)) #['小狗', '小猫', '小黑'] 2792904379712


#再添加一个属性
p.age = 10
print(p.age,id(p.age)) #10 2830603217488

#修改age属性的值
p.age = 18
print(p.age,id(p.age)) #18 2830603217744
#执行修改age后,又开辟了一块新内存放18,然后p.age去指向18,而不是在原来的地址上修改内容

4.2删除对象属性

#定义一个类
class Person:
    pass

#根据类,创建一个对象
p = Person()

p.age = 18
del p.age

5.类属性

万物皆对象,类也是一个对象

5.1添加类属性

方法一:类名.类属性 = 值

class Money:
    pass

#查看是否添加成功
Money.count = 1
print(Money.count)
print(Money.__dict__) 
#__dict__是系统内置的属性,功能是可以查看对象的所有属性和值(类也是对象)

方法二:在类中添加

class Money:
    age = 18
    count = 1
    number = 666
    
#查看是否添加成功
print(Money.age)
print(Money.count)
print(Money.number)
print(Money.__dict__)

5.2访问类属性

方法一:通过类名.类属性Money.age这种方式来查询

class Money:
    age = 18
    count = 1
    number = 666
    
#查看是否添加成功
print(Money.age)
print(Money.count)
print(Money.number)
print(Money.__dict__)

方法二:用类实例化出来的对象也可以查询类的属性,对象.类属性

class Money:
    age = 18
    count = 1
    number = 666

#创建对象
one = Money()

#给对象添加属性age
one.age = 20

#对象本身有这个属性,则优先输出本身的属性
#对象本身没有,则通过__class__去寻找类的属性
print(one.__class__) # <class '__main__.People'>
print(one.age) # 20
print(one.count) # 1
print(one.number) # 666

为什么可以通过对象访问到类属性 ?

Python对象的属性查找机制有关。优先到对象自身去查找属性,找到则结束。如果没有找到,则根据__class__找到对象对应的类,然后到这个类里面查找。

在这里插入图片描述

5.2修改类属性

通过类名来修改,不可以通过对象来修改类的属性。在对类属性进行增加,删除,修改时要使用类本身进行操作,只有进行类属性访问时,可以使用对象或类进行操作。

class Dog:
    age = 1
    
#修改类属性
Dog.age = 30

#不可以通过对象来修改类的属性,实例如下。
#创建对象
dog = Dog()

#对象中有该属性,则修改对象中的属性
#若对象中没有该属性,则直接在对象中添加该属性
dog.age = 100
print(dog.__dict__) # {'age': 100}
print(Dog.age) #30

5.3删除类属性

class Dog:
    age = 1
del Dog.age

5.4属性的内存存储

为了方便用户查看类中包含哪些属性,Python 类提供了__dict__ 属性。该属性可以用类名或者类的实例对象来调用。用类名直接调用__dict__,会输出该由类中所有类属性组成的字典;使用类的实例对象调用 __dict__ ,会输出由类中所有实例属性组成的字典。

通过对象访问属性时,其实访问的是__dict__字典里的值。一般情况下,属性存储在__dict__的字典当中,有些内置对象没有这个__dict__属性。__dict__只读的,不支持修改和赋值(可以通过setattr方法修改)。对象__dict__支持修改和赋值的。

class Money:
    age = 18
    count = 1
    num = 666

#类
Money.__dict__ = {"sex" = "男"} 
Money.__dict__["age"] = 20  
#均报错

#对象
one = Money()

#输出字典
print(one.__dict__)  #{}

#添加属性
one.__dict__ = {"name":"小明","age": 20}
print(one.__dict__) # {'name': '小明', 'age': 20}

#修改指定属性
one.__dict__['name'] = "小红"
print(one.__dict__) # {'name': '小红', 'age': 20}

5.4类属性被各个对象共享

类实例化了多个对象,这些对象都可以访问类属性,那么类属性只要一改,那所有对象访问的时候也都会跟着改变。

5.5限制对象属性的添加__slots__

默认情况下,创建一个类 ,会自动的创建一个字典__dict__来存储实例的属性。但如果定义了__slots__,类就不会再创建这个字典了。由于不存在__dict__来存储新的属性,所以使用一个不在__solts__中的属性时,程序会报错。

class Person:
    __slots__ = ['age','height']
p = Person()
p.age = 18
p.height = 180

#报错,AttributeError: 'Person' object has no attribute 'name'
p.name = "lili" 

6.方法

6.1方法的概念和作用

  1. 描述一个目标的行为动作
  2. 与函数非常类似,是类的方法函数。都封装了一系列动作,都可以被调用之后执行一系列行为动作,最主要的是调用方式(标准调用、类调用和间接调用)
#函数
def hanshu():
    print("这是一个函数")

#调用函数
hanshu()

#定义类
class Person:
    #实例方法
    def shilifangfa(self):
        print("这是一个实例方法",self)
        
#创建对象
p = Person()

#调用实例方法
p.shilifangfa()

6.2类、实例、类对象、对象的叫法规范

  1. 当从实例化的角度进行区分时,称为类和实例。类通过实例化创建一个实例。
  2. 当从对象的角度进行区分时,称为类对象和对象。从而探究他们的本质。
  3. 对于属性,可以称为类属性和实例属性。

6.3方法的划分依据

  1. 实例方法:默认第一个参数需要接收到一个实例
  2. 类方法:默认第一个参数需要接收到一个
  3. 静态方法:默认参数

注意:

  1. 划分依据是:方法第一个参数必须要接受的的数据类型。
  2. 不管是哪一种给类型方法,都储存在类当中,没有在实例当中的。
  3. 不同类型方法调用方式不同。
#定义类
class Person:
    #实例方法
    def shilifangfa(self):
        print("这是一个实例方法",self)

    #类方法
    @classmethod
    def leifangfa(cls):
        print("这是一个类方法",cls)

    #静态方法
    @staticmethod
    def jingtaifangfa():
        print("这是一个静态方法")

#创建对象
p = Person()

#调用实例方法
p.shilifangfa()
Person.shilifangfa(p)

#调用类方法
Person.leifangfa()

#调用静态方法
Person.jingtaifangfa()
p.jingtaifangfa()

#运行结果
'''
这是一个实例方法 <__main__.Person object at 0x000001FD4D1A6F70>
这是一个实例方法 <__main__.Person object at 0x000001FD4D1A6F70>
这是一个类方法 <class '__main__.Person'>
这是一个静态方法
这是一个静态方法
'''

6.4方法的储存问题

实例方法、类方法、静态方法都存储在类的__dict__字典里面。万物皆可对象,函数也是个对象,也可以被当成字典的value。

#定义类
class Person:
    #实例方法
    def shilifangfa(self):
        print("这是一个实例方法",self)

    #类方法
    @classmethod
    def leifangfa(cls):
        print("这是一个类方法",cls)

    #静态方法
    @staticmethod
    def jingtaifangfa():
        print("这是一个静态方法")
        
#实例方法,类方法,静态方法都存储在__dict__字典中
print(p.__dict__) #{}
print(Person.__dict__)
#运行结果(部分截取):
"""
{'shilifangfa': <function Person.shilifangfa at 0x000002CC8D91F820>, 
#'leifangfa': <classmethod object at 0x000002CC8D922FD0>, 
'jingtaifangfa': <staticmethod object at 0x000002CC8D922FA0>, }
"""

#万物皆对象,函数的本质也是对象
#因此也可以将函数放置在字典的value中
def run():
    print("run")

p.age = run
print(p.__dict__)
#{'age': <function run at 0x000001FD4D19F670>}

6.5实例方法

  1. 标准调用:使用实例调用实例方法,解释器会自动把调用对象本身传递给第一个参数,也就是self。除了self,其他参数加在后面就行。如果实例方法没有接受任何单数则会报错。
  2. 其他调用(不常用):类调用,间接调用。
class Person:
    def eat(self,food):
        print("我正在吃",food,self)

p = Person()
print(p) 
#<__main__.Person object at 0x0000027F32E1C0A0>

#标准调用(常用)
p.eat("土豆")
#我正在吃 土豆 <__main__.Person object at 0x0000027F32E1C0A0>

#使用类调用
Person.eat("abc","西红柿")
#我正在吃 西红柿 abc

#间接调用
func = Person.eat
func("lili","苹果")
#我正在吃 苹果 lili

6.6类方法

类可以调用类方法;实例也可以调用类方法,只是传参数的时候会把这个实例对应的类自动传给第一个参数。

class Person:
    def run(self):
        print("实例方法",self)

    @classmethod
    def leifangfa(cls,tmp):
        print("类方法",cls,tmp)
#装饰器的作用:在保证原函数不改变的前提下,直接给这个函数增加一些功能
#通过类调用
Person.leifangfa(123)

#通过实例调用
p = Person()
p.leifangfa(666)

#间接调用
func = Person.leifangfa
func(555)

#延伸类,A为Person的子类
#延伸类进行调用类方法,会将延伸类的第一个参数传输过去
class A(Person):
    pass
A.leifangfa("AAAA")
#类方法 <class '__main__.A'> AAAA

#运行结果
"""
类方法 <class '__main__.Person'> 123
类方法 <class '__main__.Person'> 666
类方法 <class '__main__.Person'> 555
类方法 <class '__main__.A'> AAAA
""""

6.7静态方法

类和实例都可以调用,解释器不会自动往里面传第一个参数。

class Person:

    @staticmethod
    def jingtai():
        print("这是一个静态方法")

#类调用
Person.jingtai()

#标准调用(实例调用)
p = Person()
p.jingtai()

#间接调用
func = Person.jingtai
func()

6.8不同类型的方法访问不同类型的属性

实例方法既可以访问实例属性,也可以访问类属性。

class Person:
    age = 18
    #实例方法
    def shilifangfa(self):
        print("我是实例方法",self)
        print("我是实例方法 类属性",self.age)
        print("我是实例方法 实例属性",self.num)

    #类方法
    @classmethod
    def leifangfa(cls):
        print("我是类方法",cls)
        print("我是类方法 类属性",cls.age)
        print("我是类方法 实例属性",cls.num)

    #静态方法
    @staticmethod
    def jingtaifangfa():
        print("我是静态方法",Person.age)

p = Person()
p.num = 100

#调用实例方法
p.shilifangfa()
Person.shilifangfa(p)

#运行结果
"""
我是实例方法 <__main__.Person object at 0x00000228AB612F10>
我是实例方法 类属性 18
我是实例方法 实例属性 100
我是实例方法 <__main__.Person object at 0x00000228AB612F10>
我是实例方法 类属性 18
我是实例方法  实例属性 100
""""

类方法只能访问类属性

class Person:
    age = 18
    #实例方法
    def shilifangfa(self):
        print("我是实例方法",self)
        print("我是实例方法",self.age)
        print("我是实例方法",self.num)

    #类方法
    @classmethod
    def leifangfa(cls):
        print("我是类方法",cls)
        print("我是类方法",cls.age)
        print("我是类方法",cls.num)

    #静态方法
    @staticmethod
    def jingtaifangfa():
        print("我是静态方法",Person.age)

p = Person()
p.num = 100

#调用类方法
p.leifangfa()
Person.leifangfa()
#AttributeError: type object 'Person' has no attribute 'num' 

7.面向对象知识点补充

7.1元类type

元类可以创建类对象,类对象可以创建实例化对象

在这里插入图片描述

寻找元类

num = 10
str1 = "lili"

class Person:
    pass

#1.寻找创建该实例化对象的类
p = Person()
print(num.__class__)
print(str1.__class__)
print(p.__class__)
# <class 'int'>
# <class 'str'>
# <class '__main__.Person'>

#2.继续寻找创建对象类的类
print(int.__class__)
print(str.__class__)
print(Person.__class__)
# <class 'type'>
# <class 'type'>
# <class 'type'>

#3.再次确认,元类type是根源。
print(type.__class__)
#<class 'type'>

创建类对象

#方法一:
class Person:
    count = 10
    def run(self):
        pass

#方法二:
#使用type,元类创建类对象

def run(self):
    print("我是元类创建的类对象",self)

#使用type创建一个Dog类,使用tmp存储(指向)该类的地址
tmp = type("Dog",(),{"count":100,"run":run})

print(tmp)
#<class '__main__.Dog'>

#使用类调用,调用Dog类中的实例方法
tmp.run("类调用")
#输出:我是元类创建的类对象 类调用

#创建对象
dog = tmp()

#使用对象调用Dog类中的实例方法
dog.run()
#输出:我是元类创建的类对象 <__main__.Dog object at 0x0000023F59C47FA0>

print(dog)
#<__main__.Dog object at 0x00000228016F7FA0>

7.2类对象创建时,元类的查找机制

类的创建流程

  1. 检测类对象是否有明确的__metaclass__属性。
  2. 检测父类中是否存在__metaclass__属性。
  3. 检测模块中是否存在__metaclass__属性。
  4. 通过内置的type这个元类来创建这个类对象
#1模块的指定
__metaclass__ = xxx
#2
class Animal:
    pass
#3
class Dog(Animal):
    pass
#4
class Dog:
    __metaclass__ = xxx
    pass

7.3类的描述

目的:

  1. 方便理清逻辑思路
  2. 方便多人合作开发时的沟通
  3. 方便生成项目文档

注意:

  1. 使用三个双引号来进行注释。
  2. 一般需要对类和方法进行注释。
  3. 使用help(类名),可以查看文档。
  4. 在类名注释中写一个Attributes来描述类属性。

ctrl + 鼠标左键查看函数内置描述

class Person:
    """
    关于这个类的描述,类的作用,类的构造函数等等;
    类属性的描述
    Attributes:  ==>属性
        count: int 代表是人的个数
    """
    # 个人的注释,而不生成文档格式
    count = 1

    def run(self, distance, step):
        """
        这个方法的作用
        :param distance: 参数的含义,参数的类型int,是否有默认值
        :param step:
        :return: 返回的结果的含义(时间),返回数据的类型int
        """
        print("奔跑中。。。")
        return distance/step

help(Person)

运行结果:

Help on class Person in module __main__:

class Person(builtins.object)
 |  关于这个类的描述,类的作用,类的构造函数等等;
 |  类属性的描述
 |  Attributes:  ==>属性
 |      count: int 代表是人的个数
 |  
 |  Methods defined here:
 |  
 |  run(self, distance, step)
 |      这个方法的作用
 |      :param distance: 参数的含义,参数的类型int,是否有默认值
 |      :param step:
 |      :return: 返回的结果的含义(时间),返回数据的类型int
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  count = 1

7.4注释文档的生成(抽取源文件的注释)

注释文档不包括源码,只包括函数名和它的描述

**方法一:**使用内置的模块pydoc

  1. 查看文档描述:python3 -m pydoc 模块名称
  2. 启动本地服务,浏览文档 :python3- m pydoc-p 1234
  3. 生成指定模块html文件:python3 -m pydoc -w 模块名称

进入本地文件,使用命令行命令进行操作。其中-m是运行该模块

python -m pydoc -h			#查看pydoc帮助文档
python -m pydoc <文件名>      #与help(类名)功能相同
python -m pydoc -k <关键字>   #列出文件名中包含关键字的文件
python -m pydoc -p <端口号>   #指定一个未用的端口号,输入b浏览,输入q退出
python -m pydoc -b			#自动指定端口号
python -m pydoc -w <文件名>   #在当前目录生成一个html文件

**方法二:**安装第三方模块。

7.5私有化属性的概念和意义

设置权限,限制在类内部访问,保证数据的安全性。例如,银行问题。

注意:Python并没有真正的私有化支持,但是,可以使用下划线完成伪私有的效果。类属性(方法)和实例属性(方法)遵循相同的规则。

属性名称/访问权限公有属性x保护属性_x私有属性__x
类内部访问
子类内部访问x
模块内其他位置访问 (父类/派生类)(父类实例/派生类实例)x
跨模块访问import√ from…import…需要__all=[]import√ from…import…需要__all=[]

7.6公共属性x

类的内部访问

class Animal:
    x = 10 #公共属性

    def test1(self): #在类的内部访问公共属性
        print(Animal.x) #通过类进行访问公共属性
        print(self.x) #通过实例化对象进行访问公共属性
        
a = Animal()
a.test1()

子类内部访问

class Animal:
    x = 10 #公共属性

    def test1(self): #在类的内部访问公共属性
        print(Animal.x) #通过类进行访问公共属性
        print(self.x) #通过实例化对象进行访问公共属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问公共属性
        print(Dog.x) #通过子类Dog进行访问公共属性
        print(self.x)#通过子类的实例化对象进行访问公共属性
        
#在类的外部访问(通过子类访问)公共属性
d = Dog()
d.test2()

模块内其他位置访问

class Animal:
    x = 10 #公共属性

    def test1(self): #在类的内部访问公共属性
        print(Animal.x) #通过类进行访问公共属性
        print(self.x) #通过实例化对象进行访问公共属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问公共属性
        print(Dog.x) #通过子类Dog进行访问公共属性
        print(self.x)#通过子类的实例化对象进行访问公共属性

a = Animal()
d = Dog()

#在本模块的其他位置访问类的公共属性
print(a.x)
print(Animal.x)
print(d.x)
print(Dog.x)

跨模块访问

导入模块:

  1. import 模块名;最常用的导入模块方式。通过使用import语句,我们可以导入整个模块,并使用模块名来访问其中的函数、类和变量等。
  2. from…import…;这种方式允许我们导入模块中的特定部分,而不是整个模块。通过这种方式,我们可以直接使用被导入部分的名称,而不需要通过模块名来访问它们。
  3. import…as…;这种方式允许我们为导入的模块或部分指定一个别名,以便在代码中使用更简短的名字。这在导入与当前代码文件位于同一目录下的自定义模块时特别有用。

区别:

  1. import module引用共享变量时,要使用module.变量名。而from module import * 直接使用变量名即可。
  2. import module方式操作module的变量时,本地不会创建新变量,操作的是目标模块的变量。from module import *方式操作module变量时,实质上是在本地创建一个与目标模块相同的变量名,并且默认与目标模块变量指向同一个内存空间。具体分析

注意:

from modname import funcfunc被直接导入到本地名字空间去了,所以它可以直接使用,而不需要加上模块名的限定* 表示,该模块的所有公共对象(public objects)都被导入到 当前的名称空间,也就是任何只要不是以”_”开始的东西都会被导入。

#模块一文件名:公共属性.py
tmp = 100
#若使用vscode引用模块一,需要使用sys库。
#文件地址是存放被引用.py文件的文件夹地址
import sys
sys.path.append('D:\PythonProgram\Python面向对象\11私有化属性')

import 公共属性
print(公共属性.tmp)

from 公共属性 import *
print(tmp)

7.7受保护属性_x

类的内部访问

class Animal:
    _x = 10 #受保护属性

    def test1(self): #在类的内部访问受保护属性
        print(Animal._x) #通过类进行访问受保护属性
        print(self._x) #通过实例化对象进行访问受保护属性
        
#在类的内部访问受保护属性
a = Animal()
a.test1()

子类内部访问

class Animal:
    _x = 10 #受保护属性

    def test1(self): #在类的内部访问受保护属性
        print(Animal._x) #通过类进行访问受保护属性
        print(self._x) #通过实例化对象进行访问受保护属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问受保护属性
        print(Dog._x) #通过子类Dog进行访问受保护属性
        print(self._x)#通过子类的实例化对象进行访问受保护属性

#在类的外部访问(通过子类访问)受保护属性
d = Dog()
d.test2()

模块内其他位置访问

class Animal:
    _x = 10 #受保护属性

    def test1(self): #在类的内部访问受保护属性
        print(Animal._x) #通过类进行访问受保护属性
        print(self._x) #通过实例化对象进行访问受保护属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问受保护属性
        print(Dog._x) #通过子类Dog进行访问受保护属性
        print(self._x)#通过子类的实例化对象进行访问受保护属性

a = Animal()
d = Dog()


#在本模块的其他位置访问类的受保护属性
print(a._x)
print(Animal._x)
print(d._x)
print(Dog._x)

跨模块访问

#模块一文件名:受保护属性.py
__all__ = ["_tmp"] #提供导出_tmp, 保证from --成功
_tmp = 100
#若使用vscode引用模块一,需要使用sys库。
#文件地址是存放被引用.py文件的文件夹地址
import sys
sys.path.append('D:\PythonProgram\Python面向对象\11私有化属性')

import 受保护属性
print(受保护属性._tmp) #成功

from 受保护属性 import *
print(_tmp) 
#1.有__all__指明对应变量,则成功 
#2.没有__all__指明对应变量,则失败

7.8私有属性__x

类的内部访问(访问成功)

class Animal:
    __x = 10 #私有属性

    def test1(self): #在类的内部访问私有属性
        print(Animal.__x) #通过类进行访问私有属性
        print(self.__x) #通过实例化对象进行访问私有属性
        
#在类的内部访问私有属性
a = Animal()
a.test1()

子类内部访问(访问失败)

class Animal:
    __x = 10 #私有属性

    def test1(self): #在类的内部访问私有属性
        print(Animal.__x) #通过类进行访问私有属性
        print(self.__x) #通过实例化对象进行访问私有属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问私有属性
        print(Dog.__x) #通过子类Dog进行访问私有属性
        print(self.__x)#通过子类的实例化对象进行访问私有属性

#在类的外部访问(通过子类访问)私有属性
d = Dog()
d.test2()

模块内其他位置访问(访问失败)

class Animal:
    __x = 10 #私有属性

    def test1(self): #在类的内部访问私有属性
        print(Animal.__x) #通过类进行访问私有属性
        print(self.__x) #通过实例化对象进行访问私有属性

class Dog(Animal): 
    
    def test2(self): #在Animal类的外部访问私有属性
        print(Dog.__x) #通过子类Dog进行访问私有属性
        print(self.__x)#通过子类的实例化对象进行访问私有属性
        
a = Animal()
d = Dog()

#在本模块的其他位置访问类的私有属性
print(a.__x)
print(Animal.__x)
print(d.__x)
print(Dog.__x)

跨模块访问

#模块一文件名:私有属性.py
__all__ = ["__tmp"] #提供导出__tmp, 保证from --成功
__tmp = 100
#若使用vscode引用模块一,需要使用sys库。
#文件地址是存放被引用.py文件的文件夹地址
import sys
sys.path.append('D:\PythonProgram\Python面向对象\11私有化属性')

import 私有属性
print(私有属性.__tmp) #成功

from 私有属性 import *
print(__tmp) 
#1.有__all__指明对应变量,则成功 
#2.没有__all__指明对应变量,则失败

7.9私有属性的实现机制

Python的私有化属性其实是伪私有化属性,只是解释器内部重新给私有化属性更换名称方式,进行名字重整,从而形成一种私有化属性保密现象,用户可通过重整后的命名方式访问,但不建议这样,毕竟目的是私有化属性,就不要再强制访问。

名字重整机制: 将__x重整为_classname__x

class Person:
    __x = 100
print(Person.__dict__)
#运行结果节选:
#{'__module__': '__main__', '_Person__x': 100}

print(Person._Person__x) # 100

目的:1. 防止外界直接访问;2. 防止被子类同名称属性覆盖。

7.10私有化属性应用场景__init__(self)

  1. 数据保护;2. 数据过滤
class Person:

    def __init__(self) -> None:
        self.__age = 18

    def setAge(self,value):
        if isinstance(value,int) and  0 < value < 120:
            self.__age = value
        else:
            print("输出有误,请重新输入")
    
    def getAge(self):
        return self.__age


p1 = Person()
p2 = Person()

p1.setAge(26)
print(p1.getAge())

print(p2.__age)
# AttributeError: 'Person' object has no attribute '__age'

7.11变量添加下划线的规范

  1. xx_:避免与系统关键字冲突
  2. __xx__:系统内置

7.12只读属性

只读属性的概念:一个属性(一般指实例属性),只能读取,不能写入。例如,电脑类的网速属性,网络状态属性。

方案一:

class Person:
    def __init__(self):
        self.__age = 20

    def getAge(self):
        return self.__age


p1 = Person()
print(p1.getAge())

方案二:

使用上面的方法来进行只读操作有两个问题:

  1. 一般属性的读取都是类似p1.age这种调用属性的方式,而这里需要调用一个函数,不是很方便。
  2. 后续使用中,如果写了一个p1.age = 50,则会让使用者误认为写入成功。而实际上是有_Person__age, age两个属性,这里只是做了一个新增。

因此,我们使用一个装饰器**@property**。这个装饰器的作用是,能够以读取属性的方式调用方法。实际上这个装饰器用于属性方法的增删改查。

#如果要使用只读功能,需要让类继承自object
#经典类–没有继承(object);新式类–继承(object)。
#python2.x版本定义一个类时,默认不继承(object)。
#python3.x版本定义一个类时,默认继承(object)。
#建议使用:养成习惯,写上object,以免python2.x报错
class Person(object):
    def __init__(self) -> None:
        self.__age = 18
    
    @property
    def age(self):
        return self.__age

p1 = Person()
print(p1.age)

p1.age = 100
#AttributeError: can't set attribute

7.13typeobject的关系

__bases__: 查看一个类型的所有父类(继承),元组形式输出。

__class__: 查看一个对象由什么实例化而来。

class Person():
    pass

p = Person()
print(Person.__bases__)
print(p.__class__)

print(object.__bases__)
print(object.__class__)

print(type.__bases__)
print(type.__class__)
#运行结果:
(<class 'object'>,)
<class '__main__.Person'>
()
<class 'type'>
(<class 'object'>,)
<class 'type'>

可以把二者理解为是两个体系的王者,object是站在继承关系顶点的存在,所有的类最后都是继承自object的,object没有父类了,所以这里输出为空()。object是type类型的,也就是说type是站在类型关系顶端的存在,所有的类型都难逃type类型的掌握,所以object和type自己的类型都是type,type的父类是object

总结:object是一个type的实例,type继承自object

7.14描述器property在新式类中的使用

官方文档中也写了有函数用法和装饰器用法

class C(object):
     def getx(self): return self._x
     def setx(self, value): self._x = value
     def delx(self): del self._x
     x = property(getx, setx, delx, "I'm the 'x' property.")
        
        Decorators make defining new properties or modifying existing ones easy:
        
class C(object):
	@property
    def x(self):
        "I am the 'x' property."
        return self._x
    @x.setter
    def x(self, value):
        self._x = value
    @x.deleter
    def x(self):
        del self._x

函数用法:

class Person(object):
    def __init__(self) -> None:
        self.__age = 18
    
    def get_age(self):
        return self.__age
    
    def set_age(self,value):
        self.__age = value

    age = property(get_age,set_age)

p = Person()
print(p.age)
p.age = 90
print(p.age)
print(p.__dict__)
"""
18
90
{'_Person__age': 90}
"""

装饰器用法:

class Person(object):

    def __init__(self) -> None:
        self.__age = 18

    @property
    def age(self):
        return self.__age
    
    @age.setter
    def age(self,value):
        self.__age = value
        
p = Person()
print(p.age)
p.age = 100
print(p.age)
print(p.__dict__)
"""
18
100
{'_Person__age': 100}
"""

在上述方法中,一个属性给了三个方法才变成一个数据解释器,如果有很多个属性,这个定义会变得很麻烦,因此,我们把三个方法封装到一个类中,后面要用到的话,就用这个类生成一个实例。

class Age:

    def __get__(self, instance, owner):
        print("get")

    def __set__(self, instance, value):
        print("set")

    def __delete__(self, instance):
        print("delete")

class Person:
    age = Age()

p = Person()
print(p.age)
p.age = 10
print(p.age)

注意:通过设置单独类设定描述器后,模块外部调用时应注意p = Person()形式实例调用,若Person.age = 19类调用,不能确保调用成功。

class Age:
    def __get__(self, instance, owner):
        print("get")

    def __set__(self, instance, value):
        print("set")

    def __delete__(self, instance):
        print("delete")

class Person:
    age = Age()

print(Person.age)
Person.age = 19
print(Person.age)
del Person.age
"""
get
None
19
"""

7.15描述器调用细节

上述结果可以发现,Person.age = 19;del Person.age语句并没有执行描述器中的相应函数,因此,使用过程中,最好使用实例调用的方法。

不能够顺利转换的场景:

  1. 描述器只能在新式类中生效,宿主类和描述器类必须全部都是新式类(object)。

  2. 方法拦截。

一个实例属性的正常访问顺序:

  1. 实例对象自身的__dict__字典

  2. 对应类对象的__dict__字典

  3. 如果有父类,会再往上层的__dict__字典中检测

  4. 如果没找到,又定义了__getattr__方法,就会调用这个方法

而在上述的整个过程中当中,是如何将描述器的__get__方法给嵌入到查找机制当中?

答: 就是通过这个方法实现:__getattribute__

内部实现模拟:如果实现了描述器方法__get__就会直接调用;如果没有,则按照上面的机制去查找。

  1. 资料描述器:实现了get,set方法

  2. 非资料描述器:只实现了get方法

优先级:资料描述器>实例属性>非资料描述器

# 资料描述器
class Age:

    def __get__(self, instance, owner):
        print('get')

    def __set__(self, instance, value):
        print('set')

    def __del__(self):
        print('del')

class Person:
    age = Age()

    def __init__(self):
        self.age = 10

p = Person()
p.age
del p.age
# 非资料描述器
class Age:

    def __get__(self, instance, owner):
        print('get')

    # def __set__(self, instance, value):
    #     print('set')
    # 
    # def __del__(self):
    #     print('del')

class Person:
    age = Age()

    def __init__(self):
        self.age = 10

p = Person()
p.age
print(p.__dict__)

描述器值的存储问题:

class Age:

    def __get__(self, instance, owner):
        print('get')
        return instance.v

    def __set__(self, instance, value):
        print('set', self, instance, value)
        instance.v = value

    def __delete__(self, instance):
        print('del')

class Person:
    age = Age()

p1 = Person()
p2 = Person()
p1.age = 11
print(p1.age)
p2.age = 12
print(p2.age)
print(p1.age)
"""
set <__main__.Age object at 0x00000269182144F0> <__main__.Person object at 0x0000026919EB0C70> 11
get
11
set <__main__.Age object at 0x00000269182144F0> <__main__.Person object at 0x0000026919EE2880> 12
get
12
get
11
"""

可以发现self对应的内容地址是相同的,也就是说,当改变任意一个p.age值时,其余的相关值都会改变,这并不是我们想要的结果,因此,应该选择instance作为我们数据存储的地址,上述结果证明,p.age之间不会相互影响。

7.16只读属性-方案二

虽然是私有属性,任然可以通过两种方法设置__age属性:

p._Person__age=xxx

p.__dict__['_Person__age']=xxx

class Person:
    #当我们通过实例.属性 = 值,给一个实力增加一个属性,或者修改属性的时候,就会自动调用这个方法,
    #在这个方法内部才会正真地把这个属性,以及对应的数据存储到__dict__字典里面
    def __setattr__(self, key, value):
        print(key, value)
        #1.判定,key,是否是我们要设置的只读属性
        if key == "age" and key in self.__dict__.keys():
           #只能新增属性,不能修改属性
           print("这个属性是只读属性,不能设置数据")
        #2.如果不是只读属性,就给他添加到实例里面去
        else:
           #self.key = value  #这样会陷入死循环
           self.__dict__[key] = value


p1 = Person()

p1.age = 18 #age 18
print(p1.__dict__) #{'age': 18}

print(p1.age) #18

p1.age = 99
#age 99
#这个属性是只读属性,不能设置数据

print(p1.__dict__) #{'age': 18}

7.17私有方法

  1. 私有方法与私有属性相同,都是在名字前面加两个下划线
  2. 名称重整机制跟私有属性也相同。
class Person(object):
    __age = 18

    def __run(self):
        print("run")

    def _Person__run(self):  # 切记不要这样去定义方法,会对私有方法进行覆盖。
        print("xxx")


p = Person()
p._Person__run()

8.内置方法

8.1常用内置属性

类属性:

  1. __dict__:类的属性
  2. __bases__:类的所有父类构成元组
  3. __doc__:类的文档字符串
  4. __name__:类名
  5. __module__:类定义所在的模块

实例属性:

  1. __dict__:实例的属性
  2. __class__:实例对应的类
class Person:
    """
    这是一个人
    """
    age = 19
    def __init__(self):
        self.name = 'lili'

    def run(self):
        print('run')

print(Person.__dict__)
print(Person.__base__)
print(Person.__doc__)
# help(Person) # 类的详细描述
print(Person.__name__)
print(Person.__module__)

p = Person()
print(p.__dict__)
print(p.__class__)
"""
{'__module__': '__main__', '__doc__': '\n    这是一个人\n    ', 'age': 19, '__init__': <function Person.__init__ at 0x00000278F382F670>, 'run': <function Person.run at 0x00000278F382F700>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
<class 'object'>

    这是一个人

Person
__main__
{'name': 'lili'}
<class '__main__.Person'>
"""

8.2格式化输出__str__

class Person:
    def __init__(self,x,y) -> None:
        self.name = x
        self.age = y

    def __str__(self) -> str:
        return "这个人的名字是%s,这个人的年龄是%s"%(self.name,self.age)
    
p1 = Person("lili","24")
p2 = Person("wawa","26")

print(p1)#是找到__str__方法对应的返回值。
print(p2)

s = str(p1) #是用str()函数找到对应实例的字符串,再赋值给s。
print(s,type(s))
"""
这个人的名字是lili,这个人的年龄是24
这个人的名字是wawa,这个人的年龄是26
这个人的名字是lili,这个人的年龄是24 <class 'str'>
"""

8.3格式化输出__repr__

  1. __repr____str__两个都是向用户输出信息。__str__一般向用户输出,__repr__一般向开发人员输出。
  2. 在实现了__str__后,print()函数就不会再输出内存信息,如果要查看内存信息,可以使用__repr__函数。
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return "这个人叫%s,他%s岁"%(self.name, self.age)


p1 = Person("lili", 24)
print(p1)
print(repr(p1))

"""
这个人叫lili,他24岁
<__main__.Person object at 0x0000019A84702FD0>
"""

#通过eval函数可以将repr内容变为str内容。
import datetime
t = datetime.datetime.now()
print(t)
print(repr(t))
print(eval(repr(t)))

__str____repr__的优先级问题?

class Person(object):

    def __init__(self,name,age) -> None:
        self.name = name
        self.age = age
    #当同时存在__str__和__repr__方法时,使用print(p)会调用__str__方法
    #当没有__str__方法时,使用print(p)才会调用__repr__方法
    #在命令行模式下,直接输入p,回车输出(不使用print),则会输出__repr__的返回值。
    def __str__(self) -> str:
        return "str"
    
    def __repr__(self) -> str:
        return "repr"

p = Person("lili",24)

print(p)
print(repr(p))

"""
str
repr
"""

8.4实例调用__call__

在创建好一个实例后,直接调用一个实例会报错。但使用__call__后,可以让这个实例可以像方法一样被调用。

class Person(object):
    def __call__(self, *args, **kwargs):
        print("这是实例调用的返回值")

p1 = Person()
p1()#实例调用

用法补充:

  1. 如果只有一个参数则默认第一个参数是 arg
  2. 如果有多个参数,但又不确定的,会作为一个元组全部传给*args
  3. 如果有对应的赋值类型的,又不确定有几对,会作为一个字典全部传给**kwargs
def func(arg, *args, **kwargs):
    print(arg)
    print(args)
    print(kwargs)

func(1, 1, 2, 3, 4, name = "lili", age = 24)
"""
1
(1, 2, 3, 4)
{'name': 'lili', 'age': 24}
"""

注意事项:

  1. 使用print()打印参数时,不用带*号。
  2. name = "lili"是这种形式,前面字符串不带引号,中间是等号。不是像字典一样用引号连接key和value。
  3. arg只接收第一个参数。
  4. 后面字典类型的参数不能分开写func(age = 24,1, 1, 2, 3, 4, name = "lili")

__call__函数应用场景:

class PenFactory:

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

    def __call__(self, p_color):
        print("创建了一个{}这个类型的画笔,它是{}颜色".format(self.p_type, p_color))

gangbiF = PenFactory("钢笔")
gangbiF('红色')
gangbiF('黄色')
gangbiF('绿色')

qianbiF = PenFactory("铅笔")
qianbiF('红色')
qianbiF('黄色')
qianbiF('绿色')

8.5索引操作

使对象具有像字典一样的索引操作

以索引的方式操作python序列:

  1. 增、改:p[1] = 666p['name'] = lili
  2. 查:p[1]p[name]
  3. 删除:del p[1]del p[name]

而在类中,通过实现三个方法,可以让类也具有这种功能。他们分别是:

def __setitem__(self, key, value):  # 设置属性
def __getitem__(self, key): # 查询属性
def __delitem__(self, key): # 删除属性

如果没有实现这三个方法,直接操作会报错:

class Person(object):
    pass

p = Person()
p["name"] = "lili"
#报错
#TypeError: 'Person' object does not support item assignment

实现了方法之后数据不会被保存:

class Person(object):

    def __setitem__(self, key, value):
        print("setitem", key, value)

    def __getitem__(self, item):
        print("getitem", item)

    def __delitem__(self, key):
        print("delitem", key)

p = Person()

p["name"] = "lili"
print(p["name"])
del p["name"]

"""
setitem name lili
getitem name
None
delitem name
"""

可以看到,方法虽然被实现,但是getitem并没有拿到实际的数据。因此可以判断数据并没有被保存下来。所以,我们需要人为的给这个实例一个储存空间。而一个字典是最为合适的,因为赋值一般会有key和value。所以,我们给所有的实例都加一个初始化好的空字典。而其中的操作都手动实现。

实例:

class Person:
    def __init__(self):
        self.cache = {}              #定义cache属性,为一个字典
    def __setitem__(self, key, value):
        self.cache[key] = value
    def __getitem__(self, key):
        return self.cache[key]
    def __delitem__(self, key):
        del self.cache[key]
p = Person()
p['name'] = 'lili'  #执行这行代码的时候,会调用第一个方法
print(p['name'])  #调用第二个方法
print(p.cache)

del p['name']
print(p.cache)

"""
lili
{'name': 'lili'}
{}
"""

8.6切片操作

索引是对一个进行增、删、改、查。

切片是对多个进行增、删、改、查。

python3中,‘切片操作’统一由‘索引操作’进行管理。切片操作跟列表的切片操作一样:

l = [1, 2, 3, 4, 5, 6]
print(l[1: 4: 2]) #[开始:结束:步长]
def __setitem__(self, key, value):  # 设置
def __getitem__(self, key): # 查询
def __delitem__(self, key): # 删除

切片实际返回的是一个slice对象:

class Person(object):
    def __setitem__(self, key, value):
        print(key, value)

p = Person()
p[0: 4: 2] = [1, 2]
#输出:slice(0, 4, 2) [1, 2]

slice对象实际上是由start,stop,step组成:

class Person(object):
    def __setitem__(self, key, value):
        print(key.start)
        print(key.stop)
        print(key.step)
        print(value)

p = Person()
p[0: 4: 2] = [1, 2]
"""
0
4
2
[1, 2]
"""
class Person:
    def __init__(self):
        self.items = [1, 2, 3, 4, 5, 6, 7, 8]  #定义了一个items属性
        
    def __setitem__(self, key, value):
        if isinstance(key,slice):
            self.items[key] = value           #切片操作一般只能修改、查询,不能新增
        #self.items.[key.start: key.stop: key.step] = value  #这样赋值也可以
    def __getitem__(self, key):
        return(key.items[key])            #这里可能有错
    def __delitem__(self, key):
        del self[key]
p = Person()
p[0: 4: 2] = ["a","b"]

print(p.items)
print(p.items[0: 4: 2])

"""
['a', 2, 'b', 4, 5, 6, 7, 8]
['a', 'b']
"""

8.7比较操作

映射的内置方法:

class Person:
    def __init__(self, age, height):
        self.age = age
        self.height = height
    def __eq__(self, other):
        return self.age == other.age   #指定相等的比较通过哪个属性比较,
    def __ne__(self, other):
        return self.age != other.age   #指定相等的比较通过哪个属性比较,
p1 = Person(18, 180)
p2 = Person(19, 183)
print(p1 == p2)
#类似的还有:
#gt >
#ge >=
#lt <
#le <=  
#可以通过调换参数的方式定义比较方法,进而简化代码

两个方法无法叠加,测试如下:

class Person(object):

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

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

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

p1 = Person(18, 180)
p2 = Person(19, 200)

print(p1 <= p2)
#报错:TypeError: '<=' not supported between instances of 'Person' and 'Person'

通过装饰器@functools.total_ordering可以实现两个方法叠加,测试如下:

import functools
@functools.total_ordering
class Person(object):
    def __init__(self, age, height):
        self.age = age
        self.height = height

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

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

p1 = Person(18, 180)
p2 = Person(19, 200)

print(p1 <= p2)
print(Person.__dict__)

上下文布尔值:

  1. 非空即为True
  2. 此方法使对象可以被作为一个布尔值使用。
class Person():
    def __bool__(self):    #通过bool值判定实例是True或False
        return True       #这里也可以是一个返回布尔值的语句
    pass

p = Person()
if p:
    print("欧耶")
#判断成绩是否及格
class Person(object):

    def __init__(self,num) -> None:
        self.num = num

    def __bool__(self):
        return self.num >= 60
    
def judge(tmp):
    if tmp:
        print("成绩合格")
    else:
        print("成绩不合格")

p1 = Person(100)
p2 = Person(45)

judge(p1)
judge(p2)

8.8遍历操作

  1. 让我们创建的对象可以使用for循环进行遍历

    1.实现__getitem__方法

    2.实现__iter__方法

  2. 让我们创建的对象可以使用next函数进行访问(迭代器)

class Person:
    def __init__(self):
        self.result = 1
    def __getitem__(self, item):
        self.result += 1
        if self.result >=6:   #不满足时,跳出循环
            raise StopIteration("停止遍历")
            
        return self.result
    pass
p = Person()

for i in p:    #当执行for循环时,自动进入__getitem__方法,使用这个方法的返回值作为数据
    print(i)

__iter__的优先级高于__getitem__

class Person:

    def __getitem__(self, item):
        print("getitem")

    def __iter__(self):
        print("iter")

p = Person()
for i in p:
    print(i)
    
#优先走到__iter__方法里面去了。但是也同时报错,提示返回值并不是一个迭代器。
"""
iter
Traceback (most recent call last):
    for i in p:
TypeError: iter() returned non-iterator of type 'NoneType'
"""

关于迭代器与可迭代:

l = [1, 2, 3, 4, 5]  # l是可迭代的,但并非迭代器
iter(l) # 使用iter()函数把可迭代的资源变成迭代器

因此,__iter__方法和iter()函数的本质都是把可迭代的数据放进去,返回一个迭代器。
而迭代器可以使用next()函数访问下一个数据。在上例中,使用for… in …则会调用实例p中的__next__方法去访问下一个元素。但在类中没有现成的,所以,要手动实现迭代器需要实现__next__方法。

本质:
可迭代的xxx -> 通过iter()或者__iter__变成迭代器 -> 通过__next__访问下一个元素

迭代器必须要有__next__方法,不然__iter__方法的return不会被识别为迭代器。

class Person(object):
    def __init__(self):
        self.result = 1

    def __iter__(self):
        return self

    def __next__(self): #没有这个会报错
        self.result += 1
        if self.result > 6:
            raise StopIteration("停止遍历")
        return self.result


p = Person()

for i in p:
    print(i)

通过next函数访问迭代器:

class Person:
    def __init__(self):
        self.result = 1
    def __getitem__(self, item):
        self.result += 1
        if self.result >=6:   #不满足时,跳出循环
            raise StopIteration("停止遍历")
        #return iter([1,2,3,4])    如果返回是迭代器,则自动进入迭代器的__next__方法   
        return relf.result
    
    def __next__(self):
        self.result += 1
        if self.result >=6:   #不满足时,跳出循环
            raise StopIteration("停止遍历")    
        return relf.result
p = person()

print(next(p))   #直接进入__next__方法
print(next(p))
print(next(p))
print(next(p))
print(next(p))   #抛出异常,停止遍历

8.9迭代器的复用

作为一个迭代器,__next__方法、__iter__方法缺一不可,少一个都会报不是迭代器。一个迭代器必然可以用next()访问,而一个可以被next()访问的不一定是一个迭代器。

class Person(object):

    def __init__(self) -> None:
        self.age = 1
    def __iter__(self):
        return self
    
    def __next__(self):
        self.age += 1
        if self.age >= 6:
            raise StopIteration("stop")
        return self.age
    
p = Person()

for i in p:
    print(i)

#使用collections包,判断p是否是一个迭代器
import collections
print(isinstance(p,collections.Iterator))

迭代器在迭代完毕后,会到最后一个元素,是没有办法自动循环从头迭代的。为解决这个问题:

class Person(object):

    def __init__(self):
        self.age = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.age += 1
        if self.age >= 6:
            raise StopIteration("stop")
        return self.age


p = Person()

for i in p:
    print(i)
for i in p:
    print(i)

8.10类装饰器

from typing import Any

class Person():
    def __init__(self,func) -> None:
        self.f = func
    def __call__(self, *args: Any, **kwds: Any) -> Any:
        print("登录验证")
        return self.f()

@Person
def fashuoshuo():
    print("发说说")

fashuoshuo()

8.11生命周期

**概念:**生命周期指的是一个对象,从诞生到消亡的过程,当一个对象被创建时,会在内存中分配相应的内存空间及逆行存储,当这个对象不再使用,为了节约内存,就会把这个对象释放。

监听对象的生命周期
__new__方法:当我们创建一个对象时,用于给这个对象分配内存的方法;通过拦截这个方法,可以修改对象的创建过程–比如:单例设计模式

class Person:
    def __new__(cls, *args, **kwargs):
        print("新建了一个对象,但是,被我拦截了")
    pass

p = Person()
print(p)

__init__方法:

class Person:

    def __init__(self):
        print("初始化方法:")
        self.name = 'lili'
    def __del__(self):
        print('这个对象被释放了')

p = Person()
del p
print(p)
print(p.name)

实验案例:

# Person,打印一下,当前这个时刻,由Person类,产生的实例,有多少个
#创建一个实例,计数+1, 如果,删除了一个实例,计数-1
personCount = 0 #使用全局变量计数
class Person:
    def __init__(self) -> None:
        global personCount
        print("计数 +1")
        personCount += 1
        
    def __del__(self):
        global personCount
        print("计数 -1")
        personCount -= 1
    
    @staticmethod
    def log():
        print("对象数量为%s",personCount)
     
p1 = Person()
p2 = Person()
Person.log()
del p1
Person.log()
"""
计数 +1
计数 +1
对象数量为%s 2
计数 -1
对象数量为%s 1
计数 -1 #进程结束,内存释放
"""
class Person:
    __personCount = 0
    def __init__(self) -> None:
        print("计数 +1")
        Person.__personCount += 1
        
    def __del__(self):
        print("计数 -1")
        self.__class__.__personCount -= 1
    
    @classmethod
    def log(cls):
        # print("对象数量为%s",Person.__personCount)
        print("对象数量为%s",cls.__personCount)

p1 = Person()
p2 = Person()
Person.log()

del p1

Person.log()

"""
计数 +1
计数 +1
对象数量为%s 2
计数 -1
对象数量为%s 1
计数 -1
"""

9.综合训练-简易计算器

简易计算器
#——————————————————————————————代码1————————————————————————————
def jia(x,y):
    return x + y

def jian(x,y):
    return x - y

def cheng(x,y):
    return x * y

r1 = jia(2,6)
r2 = jian(r1,4)
r3 = cheng(r2,5)
print(r3)

#——————————————————————————————代码2————————————————————————————

result = 0 #全局变量

def first_value(v):
    global result #强调result在函数中为全局变量
    result = v

def jia(n):
    global result #强调result在函数中为全局变量
    result += n

def jian(n):
    global result #强调result在函数中为全局变量
    result -= n

def cheng(n):
    global result #强调result在函数中为全局变量
    result *= n

first_value(2)
jia(6)
jian(4)
cheng(5)
print(result)

#——————————————————————————————代码3————————————————————————————

class Caculator:
    __result = 0 #私有属性

    @classmethod
    def first_value(cls,n):
        cls.__result = n

    @classmethod
    def jia(cls,n):
        cls.__result += n

    @classmethod
    def jian(cls,n):
        cls.__result -= n
    
    @classmethod
    def cheng(cls,n):
        cls.__result *= n
    @classmethod
    def show(cls):
        print("计算的结果是:",cls.__result)
    
Caculator.first_value(2)
Caculator.jia(6)
Caculator.jian(4)
Caculator.cheng(5)
Caculator.show()

#——————————————————————————————代码5————————————————————————————
class Caculator:

    def __init__(self,num) -> None:
        self.__result = num


    def jia( self,n):
        self.__result += n

    def jian( self,n):
        self.__result -= n  

    def cheng(self,n):
        self.__result *= n

    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码5————————————————————————————
class Caculator:
    def check_num(self,num):
        if not isinstance(num,int):
            raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")

    def __init__(self,num) -> None:
        self.check_num(num)
        self.__result = num


    def jia(self,num):
        self.check_num(num)
        self.__result += num

    def jian( self,num):
        self.check_num(num)
        self.__result -= num
    
    def cheng(self,num):
        self.check_num(num)
        self.__result *= num

    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码6————————————————————————————

class Caculator:
    #使用装饰器
    def check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    @check_num_zsq
    def __init__(self,num) -> None:
        self.__result = num

    @check_num_zsq
    def jia(self,num):
        self.__result += num

    @check_num_zsq
    def jian( self,num):
        self.__result -= num
    
    @check_num_zsq
    def cheng(self,num):
        self.__result *= num

    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码7————————————————————————————
class Caculator:
    #使用装饰器
    def __check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    @__check_num_zsq
    def __init__(self,num) -> None:
        self.__result = num

    @__check_num_zsq
    def jia(self,num):
        self.__result += num

    @__check_num_zsq
    def jian( self,num):
        self.__result -= num
    
    @__check_num_zsq
    def cheng(self,num):
        self.__result *= num

    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码8————————————————————————————

import win32com.client
#1. 创建一个播报器对象
speaker = win32com.client.Dispatch("SAPI.SpVoice")

#. 通过这个播报器对象, 直接, 播放相对应的语音字符串就可以
speaker.Speak("我的名字是李思成")
class Caculator:
    #使用装饰器
    def __check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    def create_say_zsq(word=""):
        def __say_zsq(func):
            def inner(self,num):
                speaker = win32com.client.Dispatch("SAPI.SpVoice")
                speaker.Speak(word + str(num))
                return func(self,num)
            return inner
        return __say_zsq   
     
    @__check_num_zsq
    @create_say_zsq()
    def __init__(self, num) -> None:
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("加上")
    def jia(self, num):
        self.__result += num

    @__check_num_zsq
    @create_say_zsq("减去")
    def jian( self,num):
        self.__result -= num
    
    @__check_num_zsq
    @create_say_zsq("乘以")
    def cheng(self,num):
        self.__result *= num


    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码9————————————————————————————
import win32com.client
class Caculator:
    #使用装饰器
    def __check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    def __say(self,word):
        #1.创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        # 2. 通过这个播报器对象, 直接, 播放相对应的语音字符串就可以
        speaker.Speak(word)

    def create_say_zsq(word=""):
        def __say_zsq(func):
            def inner(self,num):
                self.__say(word + str(num))
                return func(self,num)
            return inner
        return __say_zsq   
     
    @__check_num_zsq
    @create_say_zsq()
    def __init__(self, num) -> None:
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("加上")
    def jia(self, num):
        self.__result += num

    @__check_num_zsq
    @create_say_zsq("减去")
    def jian( self,num):
        self.__result -= num
    
    @__check_num_zsq
    @create_say_zsq("乘以")
    def cheng(self,num):
        self.__result *= num


    def show(self):
        print("计算的结果是:", self.__result)
    
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

#——————————————————————————————代码10————————————————————————————
import win32com.client
class Caculator:
    #使用装饰器
    def __check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    def __say(self,word):
        #1.创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        # 2. 通过这个播报器对象, 直接, 播放相对应的语音字符串就可以
        speaker.Speak(word)

    def create_say_zsq(word=""):
        def __say_zsq(func):
            def inner(self,num):
                self.__say(word + str(num))
                return func(self,num)
            return inner
        return __say_zsq   
     
    @__check_num_zsq
    @create_say_zsq()
    def __init__(self, num) -> None:
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("加上")
    def jia(self, num):
        self.__result += num

    @__check_num_zsq
    @create_say_zsq("减去")
    def jian( self,num):
        self.__result -= num
    
    @__check_num_zsq
    @create_say_zsq("乘以")
    def cheng(self,num):
        self.__result *= num

    def show(self):
        self.__say("计算的结果是:"+str(self.__result))
        print("计算的结果是:", self.__result)
    
    @property
    def result(self):
        return self.__result
c = Caculator(2)
c.jia(6)
c.jian(4)
c.cheng(5)
c.show()

print(c.result)
c.result = 10#报错,不可更改

#——————————————————————————————代码11————————————————————————————
import win32com.client
class Caculator:
    #使用装饰器
    def __check_num_zsq(func):
        def inner(self,num):
            if not isinstance(num,int):
                raise TypeError("当前这个数据的类型有问题, 应该是一个整型数据")
            return func(self,num)
        return inner
    
    def __say(self,word):
        #1.创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        # 2. 通过这个播报器对象, 直接, 播放相对应的语音字符串就可以
        speaker.Speak(word)

    def create_say_zsq(word=""):
        def __say_zsq(func):
            def inner(self,num):
                self.__say(word + str(num))
                return func(self,num)
            return inner
        return __say_zsq   
     
    @__check_num_zsq
    @create_say_zsq()
    def __init__(self, num) -> None:
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("加上")
    def jia(self, num):
        self.__result += num
        return self

    @__check_num_zsq
    @create_say_zsq("减去")
    def jian( self,num):
        self.__result -= num
        return self
    
    @__check_num_zsq
    @create_say_zsq("乘以")
    def cheng(self,num):
        self.__result *= num
        return self

    def show(self):
        self.__say("计算的结果是:"+str(self.__result))
        print("计算的结果是:", self.__result)
        return self
    
    #如果使用@property做了一个属性解释器,则不可以用下面的方法来做链式编程。
    @property
    def result(self):
        return self.__result
    
    def clear(self):
        self.__result = 0
        return self
c = Caculator(2)
#链式编程
c.jia(6).jian(4).cheng(5).show().clear().jia(5).cheng(10) 
print(c.result)

10.封装

概念:将一些属性和方法封装在一个对象中,对外隐藏内部的具体实现细节。内部的具体实现,外部不需要关心。外部只需要根据内部提供的接口去使用就可以。

作用:

  1. 使用起来更加方便:因为已经把很多相关的功能,封装成一个整体,类似于像外界提供一个工具箱,针对于不同的场景,使用不同的工具箱就可以。

  2. 保证数据的安全:针对于安全级别高的数据,可以设置成私有,可以控制数据为只读(外界无法修改),也可以拦截数据的写操作(进行数据校验和过滤)。

  3. 利于代码维护:如果后期,功能代码需要维护,则直接修改这个类内部代码即可。只要保证接口名称不变,外界不需要做任何代码修改。

11.继承

11.1继承的基本知识

概念:

  1. 通俗理解:子女继承父母的财产和资源
  2. 编程中的理解:一个类拥有另外一个类的资源的方式之一;拥有:并不是资源的复制,编程双份资源,而是,资源的使用权;资源:指非私有的属性和方法。例如Dog类继承自Animal类
  3. 被继承的类:父类,基类,超类
  4. 继承的类:子类,派生类

**作用:**便于资源的重用

分类:

  1. 单继承:

    class Animal:
        pass
    
    class Dog(Animal):
        pass
    
  2. 多继承:

    class Animal:
        pass
    
    class Dog:
        pass
    
    class JinMao(Dog,Animal):
        pass
    

查看父类的方法:Jinmao.__bases__

11.2资源的继承

在Python中,继承是指资源的使用权,所以,测试某个资源能否被继承,其实就是测试在子类当中,能不能访问到父类当中的资源。Python中的继承并不是复制,只是使用资源。子类除不能访问父类的私有的属性和私有的方法外,其余均可继承(公有属性/方法,受保护属性/方法,内置方法)。

class Animal:
    # 属性和方法
    # 设置不同权限的属性和方法,继承当中进行测试
    # 在子类当中能否访问到这些资源
    a = 1 # 公共属性
    _b = 2 # 保护属性
    __c = 3 # 私有属性

    def t1(self): # 公共方法
        print('t1')

    def _t2(self): # 保护方法
        print('t2')

    def __t3(self): # 私有方法
        print('t3')

    def __init__(self): #内置
        print("init, Animal")

class Person(Animal):
    def test(self):
        print(id(self.a))
        print(self.a)
        print(self._b)
        # print(self.__c) #报错

        self.t1()
        self._t2()
        # self.__t3() #报错

        self.__init__()

p = Person()
p.test()

print(id(Animal.a))
Animal.a = 666
p.test()
"""
init, Animal
2248234789168
1
2
t1
t2
init, Animal
2248234789168
2248240954096
666
2
t1
t2
init, Animal
"""

11.3资源的使用

继承的形态:

  1. 单继承链
  2. 无重叠的多继承链
  3. 有重叠的多继承链
# 单继承链
class C:
    pass

class B(C):
    pass

class A(B):
    pass

print(A.mro())
#print(A.__mro__)
"""
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
"""
# 无重叠的多继承链

class E:
    pass

class D:
    pass

class C(E):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.mro())
#print(A.__mro__)
"""
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>]
"""
#有重叠的多继承链-菱形结构
class D:
    pass

class C(D):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.mro())
#print(A.__mro__)
"""
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
"""

A.__mro__A.mro()查找资源查找顺序

继承链查找规则:

  1. 单继承链:从下往上(A.age 先从A自身查找,若无,B查找,若无,C查找)A==>B==>C
  2. 无重叠的多继承链:单调原则,优先先到左侧查找。(A.age先从A自身查找,若无,B中查找,如无,D中查找,若无,C中查找,如无,E中查找)A==>B==>D==>C==>E
  3. 有重叠的多继承链:从下到上,单调原则,优先上一级左,右,然后上上级。(A.age先从A自身查找,若无,B中查找,如无,C中查找,如无,D中查找)A==>B==>C==>D

11.4资源的覆盖

相同属性,子类覆盖父类。根据优先级在优先级较高内的资源写相同的资源,形成一种资源被覆盖的现象。

原理: 在MRO的资源检索链当中,优先级比较高的类写了一个和优先级比较低的类一样的一个资源(属性或方法,而摒弃优先级比较低的资源,造成”覆盖“的假象。

注意事项:当调用优先级比较高的资源时,注意self的变化。谁调用,传递谁对象。

#属性的覆盖和方法的重写
class D(object):
    age = "d"
    pass

class C(D):
    age = "c"
    def test(self):
        print("C_test")
    pass

class B(D):
    age = "b"
    def test(self):
        # print self
        print("B_test")
    @classmethod
    def test2(cls):
        print("B_test2")
    pass

class A(B, C):
    pass

A.test2()
a = A()
a.test()

print(A.mro())

print(A.age)
print(A().test())
"""
B_test2
B_test
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
b
B_test
None
"""

11.5资源的累加

概念:在一个类的基础上,增加一些额外的资源。子类相比父类,多一些自己特有的资源。在被覆盖的方法基础之上,新增内容。

class B:
    a = 1
    def __init__(self):
        self.b = 2

    def t1(self):
        print("t1")

    @classmethod
    def t2(cls):
        print("t2")

    @staticmethod
    def t3():
        print("t3")

class A(B):
    c = 3
    def __init__(self):
        self.e = "666"

    def tt1(self):
        print("tt1")

    @classmethod
    def tt2(cls):
        print("tt2")

    @staticmethod
    def tt3():
        print("tt3")

a_obj = A()

print(A.a)
# print(a_obj.b) 
#该段语句报错,在存在自己的初始化函数__init__时,不会调用父类的初始化函数
#解决办法:
# 1.直接复制父类的内容到子类初始化函数内
# 2.通过super

a_obj.t1()
A.t2()
A.t3()

print(A.c)

a_obj.tt1()
A.tt2()
A.tt3()

a_obj.d = "xxx"
print(a_obj.d)
print(a_obj.e)
"""
1
t1
t2
t3
3
tt1
tt2
tt3
xxx
666
"""

11.6super

语句形式:

# 适用于新式类
# Python2.2+
super(type,obj)
# obj当作另外一个实例方法里的参数传递,type是类

super(type, type2)
# 调用其他类的类方法,传递给类方法的第一个参数cls

# python 3+
super() #自动输入

通过super调用父类中的方法,注意:第一个参数是类函数,第二个参数是指传递的参数。第一个参数指查找哪一个类的下一个节点,使用第二个参数的MRO链条去调用后续的方法。

class B:
    a = 1

    def __init__(self):
        self.b = 2
        self.xxx = "123"

    def t1(self):
        print("t1")

    @classmethod
    def t2(cls):
        print(cls)
        print("t2")

    @staticmethod
    def t3():
        print("t3")

class A(B):
    c = 3

    def __init__(self):
        # super(A, self).__init__()
        super().__init__()
        self.e = "666"

    def tt1(self):
        print("tt1")

    @classmethod
    def tt2(cls):
        super(A, cls).t2()
        print("tt2")

    @staticmethod
    def tt3():
        print("tt3")

a = A()
print(a.__dict__)
A.tt2()
"""
{'b': 2, 'xxx': '123', 'e': '666'}
<class '__main__.A'>
t2
tt2
"""

菱形问题:

class D(object):
    def __init__(self):
        print("d")

class B(D):
    def __init__(self):
        # super().__init__()
        super(B, self).__init__()
        # 切记不要用self.__class__代替B,因为self不一定是B中的类,有可能是子类传递过来的self 
        print("b")

class C(D):
    def __init__(self):
        # super().__init__()
        super(C, self).__init__()
        print("c")

class A(B, C):
    def __init__(self):
        super().__init__()
        # B.__init__(self) # 如果分开写,会出现重复调用
        # C.__init__(self)
        print("a")

A()
print(A.mro())
"""
d
c
b
a
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
"""

12.多态

**概念:**一个类,所延伸的多种形态;

**调用时的多种形态:**在继承的前提下,使用不同的子类,调用父类的同一个方法,产生不同的功能

class Animals:
    def jiao(self):
        pass

class Dog(Animals):
    def jiao(self):
        print('Wangwang')

class Cat(Animals):
    def jiao(self):
        print('Miaomiao')

def test(obj):
    obj.jiao()

d = Dog()
c = Cat()
test(d)
test(c)
"""
Wangwang
Miaomiao
"""

多态在Python中的体现

  1. 鸭子类型:
    • 动态类型的一种风格。
    • 只要一个对象,会走,会游泳,会叫。那么它就可以当作一个鸭子进行处理。
    • 关注点在于对象的行为和属性,而非对象的类型
  2. 所以,在Python当中,没有真正意义上的多态,也不需要多态。

13.抽象类和抽象方法

**抽象类:**一个抽象出来的类,并不是某一个具化的类。不能直接创建实例的类,创建会报错。

**抽象方法:**抽象出来的一个方法,不具备具体实现,不能直接调用;子类中若不存在该方法,直接报错。

Python中的实现

  1. 无法直接支持,需要借助一个模块:import abc
  2. 设置类的元类为abc.ABCMeta
  3. 使用装饰器修饰抽象方法@abc.abcstractmethod
import abc

class Animals(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def jiao(self):
        pass

    @abc.abstractclassmethod
    def test(cls):
        print('cls')

class Dog(Animals):
    def jiao(self):
        print('Wangwang')

    @classmethod
    def test(cls):
        print('Dogcls')
    pass

class Cat(Animals):
    def jiao(self):
        print('Miaomiao')

def test(obj):
    obj.jiao()

d = Dog()
# c = Cat() #报错
test(d)
# test(c)
d.jiao()
d.test()
"""
Wangwang
Wangwang
Dogcls
"""
#在类中使用abc模块中的方法、静态方法和类方法时,声明之后必须要在运行的子类中也有相应的方法,否则无法运行。

14.三大特性综合练习

# 定义三个类, 小狗, 小猫, 人
# 小狗: 姓名, 年龄(默认1岁);        吃饭, 玩, 睡觉, 看家(格式: 名字是xx, 年龄xx岁的小狗在xx)
# 小猫: 姓名, 年龄(默认1岁);        吃饭, 玩, 睡觉, 捉老鼠(格式: 名字是xx, 年龄xx岁的小猫在xx)
# 人:   姓名, 年龄(默认1岁), 宠物;  吃饭, 玩, 睡觉(格式: 名字是xx, 年龄xx岁的人在xx)
#                           养宠物(让所有的宠物吃饭, 玩, 睡觉),
#                           让宠物工作(让所有的宠物根据自己的职责开始工作)

#——————————————————————————————代码1————————————————————————————
class Dog:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age
    
    def eat(self):
        print("名字是%s,年龄%d岁的小狗在吃饭" % (self.name,self.age))

    def play(self):
        print("名字是%s,年龄%d岁的小狗在玩儿" % (self.name,self.age))

    def sleep(self):
        print("名字是%s,年龄%d岁的小狗在睡觉" % (self.name,self.age))
    
    def watch(self):
        print("名字是%s,年龄%d岁的小狗在看家" % (self.name,self.age))

class Cat:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age

class People:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age

d = Dog("大黄")
d.eat()

#——————————————————————————————代码2————————————————————————————
class Dog:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age
    
    def eat(self):
        print("%s在吃饭" % self)

    def play(self):
        print("%s在玩儿" % self)

    def sleep(self):
        print("%s在睡觉" % self)
    
    def watch(self):
        print("%s在看家" % self)

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的小狗" % (self.name,self.age)

class Cat:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age

class People:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age

d = Dog("大黄",2)
d.eat()
d.play()


#——————————————————————————————代码3————————————————————————————
class Dog:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age
    
    def eat(self):
        print("%s在吃饭" % self)

    def play(self):
        print("%s在玩儿" % self)

    def sleep(self):
        print("%s在睡觉" % self)
    
    def watch(self):
        print("%s在看家" % self)

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的小狗" % (self.name,self.age)

class Cat:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age
    
    def eat(self):
        print("%s在吃饭" % self)

    def play(self):
        print("%s在玩儿" % self)

    def sleep(self):
        print("%s在睡觉" % self)
    
    def catch(self):
        print("%s抓老鼠" % self)

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的小猫" % (self.name,self.age)

class People:

    def __init__(self,name,pets,age = 1) -> None:
        self.name = name
        self.pets = pets
        self.age = age
    
    def eat(self):
        print("%s在吃饭" % self)

    def play(self):
        print("%s在玩儿" % self)

    def sleep(self):
        print("%s在睡觉" % self)

    def yangPets(self):
        for pet in self.pets:
            pet.eat()
            pet.play()
            pet.sleep()

    def makePetsWork(self):
        for pet in self.pets:
            if isinstance(pet,Dog):
                pet.watch()
            elif isinstance(pet,Cat):
                pet.catch()

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的人" % (self.name,self.age)

d = Dog("大黄",2)
c = Cat("丧彪",5)
p = People("lili",[d,c],24)
p.yangPets()
p.makePetsWork()


#——————————————————————————————代码4————————————————————————————
class Animal:

    def __init__(self,name,age = 1) -> None:
        self.name = name
        self.age = age
    
    def eat(self):
        print("%s在吃饭" % self)

    def play(self):
        print("%s在玩儿" % self)

    def sleep(self):
        print("%s在睡觉" % self)

class Dog(Animal):
  
    def work(self):
        print("%s看家" % self)

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的小狗" % (self.name,self.age)

class Cat(Animal):

    def work(self):
        print("%s抓老鼠" % self)

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的小猫" % (self.name,self.age)

class People(Animal):

    def __init__(self,name,pets,age = 1) -> None:
        super().__init__(name,age)
        self.pets = pets

    def yangPets(self):
        for pet in self.pets:
            pet.eat()
            pet.play()
            pet.sleep()

    def makePetsWork(self):
        for pet in self.pets:
            pet.work()

    def __str__(self) -> str:
        return "名字是%s,年龄%d岁的人" % (self.name,self.age)

d = Dog("大黄",2)
c = Cat("丧彪",5)
p = People("lili",[d,c],24)
p.play()
p.yangPets()
p.makePetsWork()
"""
名字是lili,年龄24岁的人在玩儿
名字是大黄,年龄2岁的小狗在吃饭
名字是大黄,年龄2岁的小狗在玩儿
名字是大黄,年龄2岁的小狗在睡觉
名字是丧彪,年龄5岁的小猫在吃饭
名字是丧彪,年龄5岁的小猫在玩儿
名字是丧彪,年龄5岁的小猫在睡觉
"""

完美撒花
侃侃: 如果文章对您有帮助,别忘了点个赞点个关注
完美撒花 ❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/660467.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数组-给出最大容量,求能获得的最大值

一、问题描述 二、解题思路 这个题目其实是求给出数组中&#xff0c;子数组和不大于M中&#xff0c;和最大值的子数组。 求子数组使用双指针就可以解决问题&#xff0c;相对比较简单。&#xff08;如果是子序列&#xff0c;则等价于0-1背包问题&#xff0c;看题目扩展中的问题…

Node性能如何进行监控以及优化?

一、 是什么 Node作为一门服务端语言&#xff0c;性能方面尤为重要&#xff0c;其衡量指标一般有如下&#xff1a; CPU内存I/O网络 CPU 主要分成了两部分&#xff1a; CPU负载&#xff1a;在某个时间段内&#xff0c;占用以及等待CPU的进程总数CPU使用率&#xff1a;CPU时…

Vue 3 教程:核心知识

Vue 3 教程&#xff1a;核心知识 1. Vue3简介1.1. 【性能的提升】1.2.【 源码的升级】1.3. 【拥抱TypeScript】1.4. 【新的特性】 2. 创建Vue3工程2.1. 【基于 vue-cli 创建】2.2. 【基于 vite 创建】(推荐)2.3. 【一个简单的效果】 3. Vue3核心语法3.1. 【OptionsAPI 与 Compo…

JDK8新特性之lambda表达式

大家好&#xff0c;这里是教授.F 目录 引入&#xff1a; 什么情况下使用/使用前提&#xff1a; 标准格式&#xff1a; 实现原理&#xff1a; 省略格式&#xff1a; 引入&#xff1a; lambda表达式就是简介的书写我们的匿名内部类。Lambda相当于对接口的抽象方法的重写。 什…

盐化行业数字化转型规划详细方案(124页PPT)

方案介绍&#xff1a; 本项目旨在通过引入先进的信息技术和数字化手段&#xff0c;对盐化行业的生产、管理、销售等各个环节进行全面优化和升级&#xff0c;实现生产效率提升、成本降低、产品质量提升、市场竞争力增强等目标。此外环保水平得到提升&#xff0c;降低企业的环保…

Opencv图像处理技术(图像轮廓)

1图像轮廓概念&#xff1a; 图像轮廓是指图像中连续的像素边界&#xff0c;这些边界通常代表了图像中的物体或者物体的边缘。在数字图像处理中&#xff0c;轮廓是由相同像素值组成的曲线&#xff0c;它们连接相同的颜色或灰度值&#xff0c;并且具有连续性。轮廓可以用来描述和…

滴滴一季度营收同比增长14.9%至491亿元 经调整EBITA盈利9亿元

【头部财经】5月29日&#xff0c;滴滴在其官网发布2024年一季度业绩报告。一季度滴滴实现总收入491亿元&#xff0c;同比增长14.9%&#xff1b;经调整EBITA&#xff08;非公认会计准则口径&#xff09;盈利9亿元。其中&#xff0c;中国出行一季度实现收入445亿元&#xff0c;同…

VSCode小技巧,忽略不想格式化的代码行

零&#xff0e;格式化工具文档 1 . Black Ignoring sections功能 2 . autopep8 disabling-line-by-line功能&#xff1b;&#xff1b;–line-range选项 3 . Prettier prettier-ignore功能(例&#xff1a;适用于JS的// prettier-ignore&#xff0c;适用于CSS的/* prettier-igno…

02_变量提升与函数提升及其优先级(JS高级)

目录 一、 变量提升与函数提升 为什么要进行变量提升和函数提升 1.1 变量提升 1.2 函数提升 1.3 变量提升与函数提升的优先级 二、执行上下文 三、执行上下文栈 四、习题 一、 变量提升与函数提升 为什么要进行变量提升和函数提升 JS引擎在读取js代码的过程中&#xf…

Dinky MySQLCDC 整库同步到 Doris

资源&#xff1a;flink 1.17.0、dinky 1.0.2、doris-2.0.1-rc04 问题&#xff1a;Cannot deserialize value of type int from String &#xff0c;detailMessageunknowndatabases &#xff0c;not a valid int value 2024-05-29 16:52:20.136 ERROR org.apache.doris.flink.…

OpenAI开始训练新的前沿模型——但GPT-5至少在90天内不会推出

ChatGPT 制造商 OpenAI 今早宣布&#xff0c;已开始训练其新的“前沿模型”&#xff0c;并成立了一个新的安全委员会&#xff0c;由现任董事会成员 Bret Taylor&#xff08;OpenAI 董事会主席兼客户服务初创公司 Sierra AI 联合创始人、前谷歌地图负责人和前 Facebook 首席技术…

DNSlog环境搭建

阿里云域名公网VPS地址 购买阿里云域名后设置“自定义DNSHOST” DNS服务器填写ns1和ns2 如&#xff1a;ns1.aaa.com IP地址填写你的VPS地址 如&#xff1a;1.1.1.1 填写解析记录&#xff0c;一个A记录、一个NS记录 NS记录就是*.域名指向记录值ns1.域名 如&#xff1a;*.aaa…

JVM之加载class文件

1.JVM相关概念 官网地址:Java Platform Standard Edition 8 Documentation (oracle.com) jvm: java虚拟机&#xff0c;是java程序的运行环境(java二进制字节码的运行环境) jre:jvm基础类库(java.lang包下工具类、IO、集合类库、线程类等)组成了完整的运行环境 jdk:是由jvmj…

ElementUI之el-table标题列中显示el-tooltip

ElementUI之el-table标题列中显示el-tooltip 文章目录 ElementUI之el-table标题列中显示el-tooltip1. el-table标题列中显示el-tooltip2. 实现代码3. 展示效果 1. el-table标题列中显示el-tooltip 在el-table-column标签内添加具名插槽v-slot:header 在el-tooltip标签中使用具…

AI企业需要“联盟营销”?一文带你探索AI企业营销新玩法!

为什么联盟营销对AI业务有较大优势 联盟营销在电商领域、saas领域与其他产品领域同样有效。在AI业务中&#xff0c;它有效的原因与其他领域大不相同。 高好奇心和试用率 AI领域是创新的热点。它吸引了一群渴望探索和尝试每一项新技术的人群。这种蓬勃的好奇心为聪明的AI企业提…

NTP服务的DDoS攻击:原理和防御

NTP协议作为一种关键的互联网基础设施组件&#xff0c;旨在确保全球网络设备间的时钟同步&#xff0c;对于维护数据一致性和安全性至关重要。然而&#xff0c;其设计上的某些特性也为恶意行为者提供了发动大规模分布式拒绝服务(DDoS)攻击的机会。以下是NTP服务DDoS攻击及其防御…

34岁嵌入式开发工程师的出路在哪儿?

作为一个从事智能穿戴行业11年的资深从业者&#xff0c;您积累了丰富的技术和经验&#xff0c;IT行业内有很多发展机会和出路可以选择&#xff0c;以下是一些建议供参考&#xff1a;刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到…

uniapp开发vue3监听右滑返回操作,返回到指定页面,解决边缘区域监听错误问题

想要在uniapp框架中监听左滑或者右滑手势&#xff0c;需要使用touchstart和touchend两个api&#xff0c;因为没有原生的左右滑监听api&#xff0c;所以我们只能依靠这两个api来获取滑动开始时候的x坐标和滑动结束后的x坐标做比对&#xff0c;右滑的话&#xff0c;结束时候的x坐…

gitea的git库备份与恢复

文章目录 gitea库的备份与恢复概述笔记实验环境更新git for windows更新 TortoiseGit备份已经存在的gitea的git库目录使用gitea本身来备份所有git库目录将gitea库恢复到新目录m1m2m3启动gitea - 此时已经恢复完成FETCH_HEAD 中有硬写位置再查一下app.ini, 是否改漏了。m1m2 总结…

pytorch深度学习-环境搭建-2

1.1下载cudnn,解压 1.2.找到本级cuda安装路径 1.3.刚才解压文件复制到cuda安装目录 2.1 安装pytouch conda install pytorch torchvision torchaudio pytorch-cuda12.1 -c pytorch -c nvidia 3.pytouch验证 我这儿是有问题的 PS C:\Users\Administrator\PycharmProjects\pyth…