Python-面向对象编程(超详细易懂)

面向对象编程(oop)

       面向对象是Python最重要的特性,在Python中一切数据类型都是面向对象的。

       面向对象的编程思想:按照真实世界客观事物的自然规律进行分析,客观世界中存在什么样的实体,构建的软件系统就存在什么样的实体。

       例如:在学校里,有学生实体,学生是抽象的描述,称为“类”;而张同学、李同学等具体的学生个体称为“对象”,也称为“实例”;学生有学号、姓名、班级等属性,还有吃饭、走路等操作。

面向对象三大特性:封装性、继承性、多态性

封装性:使外部访问者不能随意存取对象的内部数据,隐藏了对象的内部细节,只保留有限的对外接口。外部访问者不用关心对象的内部细节,操作对象变得简单。

继承性: 特殊类继承一般类,拥有一般类的全部数据和操作。一般类称为“父类”或“超类”,特殊类称为“子类”或“派生类”。

多态性:在父类中成员被子类继承后,可以具有不同的状态或表现行为。

类和对象

       Python中的数据类型都是类,类是组成Python程序的基本要素,它封装了一个类对象的数据和操作。

定义类

Python语言中一个类的实现包括类定义和类体。语法格式如下:

class 类名[(父类)]:类体

 输入下面代码

class Animal(): # 创建Animal类pass
class Books(): # #创建Books类pass# 分别创建对应的类对象
a = Animal()
b = Books()print(id(Animal))
print(type(Animal))
print(type(a))
print(type(b))

代码运行结果

1939387281408
<class 'type'>
<class '__main__.Animal'>
<class '__main__.Books'>Process finished with exit code 0

       注意:object类是所有类的根类,在Python中任何一个类都直接或间接的继承object;代码的pass语句表示什么操作都不执行,用来维持程序结构的完整。

创建、使用和销毁对象

       一个对象的生命周期包括创建、使用和销毁。类实例化可生成对象,所以对象也称为“实例”。

       创建对象就是在类对象的后面加上一对小括号,表示调用类对象的构造方法,这就创建了一个对象,示例代码如下

class Animal:pass
'''
Animal()表达式创建了一个对象,并把创建的对象通过“=”赋给animal变量
animal是指向对象的一个引用。通过animal可以使用刚刚创建的对象
'''
animal = Animal()
# print函数实际上是调用了对象的__str__()方法(__str__()是object类的一个方法,
# 用于返回有关该对象的描述信息)输出字符串信息
print(animal)

代码运行结果

<__main__.Animal object at 0x000001A74F1F1970>

销毁对象

       Python会实现自动的垃圾回收:当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__()(析构函数),__del__()被称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源(例如:打开的文档资源、网络连接等)。我们也可以通过del语句删除对象,系统会自动调用__del__()方法,一般不需要自定义析构方法。

示例代码如下

class Person:def __del__(self):print("销毁对象:{0}".format(self))
p1 = Person()
p2 = Person()
del p2
'''
当 p2 的引用被 del 语句删除时
p2 的 __del__ 方法会被调用
但是,p1 的 __del__ 方法可能在程序结束前不会被调用
因为它的引用仍然存在于变量 p1 中
'''
print("程序结束")

代码运行结果

销毁对象:<__main__.Person object at 0x000001B649C01AF0>
程序结束
销毁对象:<__main__.Person object at 0x000001B649C01AC0>

 实例变量

“实例变量”就是某个实例(或对象)个体特有的数据。

示例代码如下

class Animal(object):def __init__(self,name,age,weight,gender):self.name = name # 定义姓名实例变量self.age= age # 定义年龄实例变量self.weight = weight # 定义体重实例变量self.gender =gender   # 定义性别实例变量
animal = Animal('Tom',18,66,1)
animal1 = Animal('Mary',18,50,0)print("姓名:{0},年龄:{1},体重:{2}".format(animal.name,animal.age,animal.weight))
print("男性" if animal.gender==1 else '女性')
print("姓名:{0},年龄:{1},体重:{2}".format(animal1.name,animal1.age,animal1.weight))
print("男性" if animal1.gender==1 else '女性')

代码运行结果

姓名:Tom,年龄:18,体重:66
男性
姓名:Mary,年龄:18,体重:50
女性

 类变量

       “类变量”是所有实例(或对象)共有的变量,需要在方法外定义,访问实例变量通过“实例名.实例变量名”的形式,而访问类变量“类名.类变量名”的形式访问。

示例代码如下

class Account:interest_rate = 0.068 # 类变量
# 重写构造方法def __init__(self,owner,amount):self.owner =  ownerself.amount = amount
account = Account('Tony',1_800_000.0)
# 查看实例变量
print('account实例所有变量:{0}'.format(account.__dict__))
# 为account.interest_rate变量赋值,此时无论类中是否有同名变量都会创建一个新的实例变量
account.interest_rate = 0.01
account.interest_rate2 = 0.01
print('账户名:{0}'.format(account.owner))
print('账号金额:{0}'.format(account.amount))
print('利率:{0}'.format(Account.interest_rate))
# 查看实例变量
print('account实例所有变量:{0}'.format(account.__dict__))

代码运行结果

account实例所有变量:{'owner': 'Tony', 'amount': 1800000.0}
账户名:Tony
账号金额:1800000.0
利率:0.068
account实例所有变量:{'owner': 'Tony', 'amount': 1800000.0, 'interest_rate': 0.01, 'interest_rate2': 0.01}

       注意:不要通过实例存取类变量数据。当通过实例读取变量时,Python解释器会先在实例中找这个变量,如果没有再到类中去找;当通过实例为变量赋值时,无论类中是否有该同名变量,Python解释器都会创建一个同名实例变量。 

构造方法

       __init_()方法是构造方法,用来创建和初始化实例变量,定义时它的第一个参数应该是self,其后的参数才是用来初始化实例变量的;其中参数可以有默认值。调用构造方法时不需要传入self。如果未定义构造方法,则会自动调用默认的构造方法;一旦定义了构造方法,则不会调用默认的构造方法。

输入下面代码

class Animal(object):# 构造方法def __init__(self,name='mark',age=20,weight=120,gender=120):self.name = name # 定义姓名实例变量self.age= age # 定义年龄实例变量self.weight = weight # 定义体重实例变量self.gender =gender   # 定义性别实例变量
animal0 = Animal() # 此时没有传入参数,参数为默认值
animal = Animal('Tom',18,66,1)
animal1 = Animal('Mary',18,50,0)print("姓名:{0},年龄:{1},体重:{2}".format(animal0.name,animal0.age,animal0.weight))
print("姓名:{0},年龄:{1},体重:{2}".format(animal.name,animal.age,animal.weight))
print("姓名:{0},年龄:{1},体重:{2}".format(animal1.name,animal1.age,animal1.weight))

代码运行结果

姓名:mark,年龄:20,体重:120
姓名:Tom,年龄:18,体重:66
姓名:Mary,年龄:18,体重:50

实例方法

       实例方法与实例变量一样都是某个实例个体特有的。定义实例方法时它的第一个参数应该是self,这个过程是将当前实例与该方法绑定起来,使该方法成为实例方法。

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.weight = weightdef eat(self):self.weight += 0.05print("eat...")def run(self):self.weight -= 0.01print("run...")
a1 = Animal(2,0,10.0)
print('a1体重:{0:0.2f}'.format(a1.weight))
a1.eat()
print('a1体重:{0:0.2f}'.format(a1.weight))
a1.run()
print('a1体重:{0:0.2f}'.format(a1.weight))

代码运行结果

a1体重:10.00
eat...
a1体重:10.05
run...
a1体重:10.04

类方法

       类方法不需要与实例绑定,但需要与类绑定,定义时它的第一个参数是类的type实例。type是描述Python数据类型的类,Python中所有数据类型都是type的一个实例。类方法可以访问类变量和其他类方法,但不能访问其他实例方法和实例变量。

       定义类方法:方法第一个参数cls是type类型的一个实例;方法使用装饰器@classmethod声明该方法是类方法。该方法不能调用实例属性,也不能调用实例方法。

示例代码如下

class Account:interest_rate = 0.0668 # 类变量
# 重写构造方法def __init__(self,owner,amount):self.owner =  ownerself.amount = amount# 类方法@classmethoddef interest_by(cls,amt):return cls.interest_rate * amtinterest = Account.interest_by(12_000.0)
print('计算利息:{0:.4f}'.format(interest))

代码运行结果

计算利息:801.6000

静态方法

       如果定义的方法既不想与实例绑定,也不想与类绑定,只是想把类作为它的命名空间,那么可以定义静态方法。定义时使用@staticmethod装饰器,声明方法是静态方法,方法参数不指定self 和 cls。该方法不能调用实例属性,也不能调用实例方法。

示例代码如下

class Account:interest_rate = 0.0668 # 类变量
# 重写构造方法def __init__(self,owner,amount):self.owner =  ownerself.amount = amount# 类方法@classmethoddef interest_by(cls,amt):return cls.interest_rate * amt@staticmethoddef interst_with(amt):return Account.interest_by(amt)
interest1 = Account.interest_by(12_000.0)
print('计算利息:{0:.4f}'.format(interest1))
interest2 = Account.interest_by(12_000.0)
print('计算利息:{0:.4f}'.format(interest2))

代码运行结果

计算利息:801.6000
计算利息:801.6000

动态绑定属性和方法 

示例代码如下

class Person():def __init__(self,name,age):self.name = nameself.age = agedef show(self):print(f'我的名字是{self.name},我的年龄是{self.age}')person1 = Person('张三',18)
person2 = Person('李四',19)# 为person2动态绑定属性
person2.gender = 'male'
# 为person2动态绑定方法
def method():print('这是用于动态绑定的方法')
person2.fun = method
# 输出绑定的属性
print(person2.gender) # person1并没有gender属性
person2.fun() # person1并没有fun()方法

代码运行结果

male
这是用于动态绑定的方法Process finished with exit code 0

封装性

私有变量

       默认情况下,Python中的变量是公有的,可以在类的外部访问他们。如果想让他们成为私有变量,可以在变量前加上双下划线“__”,双下划线开头表示private私有的成员,这类成员只允许定义该属性或方法的类本身进行访问。如果以单下划线开头的属性或方法表示protected受保护的成员,这类成员被视为仅供内部使用,允许类本身和子类进行访问,但实际上它可以被外部代码访问。而首尾双下划线一般表示特殊的方法。

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__wight = weightdef eat(self):self.__wight += 0.05print("eat...")def run(self):self.__wight -= 0.01print("run...")a1 = Animal(2,0,10.0) # 该段代码发生错误,因为weight是私有变量,无法在类的外部访问
print('a1体重:{0:0.2f}'.format(a1.weight))
a1.eat()
a1.run()

代码运行结果

       Python语言并不像Java,私有变量只是形式上私有,并非严格意义上的私有;如果想要访问私有变量,可以通过  对象名._类名__变量名 来访问,但是一般不推荐这么做,因为这违反了封装的原则,使代码的维护变得困难(可见Python的封装性靠的是程序员的自律,而非强制性的语法)。

输入下面代码

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量def eat(self):self.__weight += 0.05print("eat...")def run(self):self.__weight -= 0.01print("run...")a1 = Animal(2,0,10.0)
print('a1体重:{0:0.2f}'.format(a1._Animal__weight))
a1.eat()
a1.run()
# 通过内置函数dir打印对象的属性和方法
print(dir(a1))

代码运行结果

a1体重:10.00
eat...
run...
['_Animal__weight', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'eat', 'run', 'sex']

推荐的做法是定义一个公共方法来访问

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量def eat(self):self.__weight += 0.05print("eat...")def run(self):self.__weight -= 0.01print("run...")def get_weight(self): # 添加一个公共方法来获取私有变量的值return self.__weighta1 = Animal(2,0,10.0)
print('a1体重:{0:0.2f}'.format(a1.get_weight()))
a1.eat()
a1.run()
print('a1体重:{0:0.2f}'.format(a1.get_weight()))

代码运行结果

a1体重:10.00
eat...
run...
a1体重:10.04

私有方法

       私有方法与私有变量是类似的,只要在方法前加上双下划线“__”就是私有方法,在类外面强制使用也是“对象名._变量名__方法名”(不推荐)。

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量def eat(self):self.__weight += 0.05print("eat...")def __run(self):self.__weight -= 0.01print("run...")def get_weight(self): # 添加一个公共方法来获取私有变量的值return self.__weighta1 = Animal(2,0,10.0)
print('a1体重:{0:0.2f}'.format(a1.get_weight()))
a1.eat()
a1._Animal__run() #强制访问,不推荐这样做
print('a1体重:{0:0.2f}'.format(a1.get_weight()))

代码运行结果

a1体重:10.00
eat...
run...
a1体重:10.04

定义属性

       在实际的面向对象设计中,一个类是不应该有公有的实例成员变量的,这些实例成员变量被设计为私有的,然后通过共有的 setter 和 getter 访问器访问。(访问器形式的封装需要一个私有变量,需要提供 setter 和 getter 访问器,只读变量不用提供 setter 访问器)

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量def eat(self):self.__weight += 0.05print("eat...")def __run(self):self.__weight -= 0.01print("run...")def get_weight(self): # 添加一个公共方法来获取私有变量的值return self.__weightdef set_weight(self,weight): # 添加一个set方法来访问self.__weight = weighta1 = Animal(2,0,10.0) # 该段代码发生错误,因为weight是私有变量,无法在类的外部访问
print('a1体重:{0:0.2f}'.format(a1.get_weight()))
a1.set_weight(20.00)
print('a1体重:{0}'.format(a1.get_weight()))

代码运行结果

a1体重:10.00
a1体重:20.0

       访问器形式的封装在编写代码时比较麻烦,Python提供了属性(poperty),定义属性可以使用@property(将一个方法转换成属性去使用,即定义形式是方法,使用时可以将该方法当成属性来用,也就是不需要加括号)和 @属性名.setter装饰器(通过@property设置之后就只能访问不能修改,如果想要修改可以再定义@属性名.setter方法)。

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量@propertydef weight(self): # 添加一个公共方法来获取私有变量的值return self.__weight@weight.setterdef weight(self,weight): # 添加一个set方法来访问self.__weight = weighta1 = Animal(2,0,10.0) # 该段代码发生错误,因为weight是私有变量,无法在类的外部访问
print('a1体重:{0:0.2f}'.format(a1.weight))
a1.weight = 20.0
print('a1体重:{0}'.format(a1.weight))

代码运行结果

a1体重:10.00
a1体重:20.0

示例代码二:加深对装饰器的理解

示例代码如下

class Person():def __init__(self,name,age):self.name = nameself.__age = age@propertydef age(self):return self.__age@age.setterdef age(self,value):if value<0 or value>150:print('年龄不在有效范围,已将年龄设置为默认18')self.__age = 18else:self.__age = valueperson = Person('张三',18)
print(person.name,person.age)
person.age = 190
print(person.age)

 代码运行结果

张三 18
年龄不在有效范围,已将年龄设置为默认18
18Process finished with exit code 0

 继承性

       多态性的前提是继承性,子类继承父类时只是继承父类中公有的成员变量和方法,不能继承私有的成员变量和方法。

示例代码如下

class Person:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.__weight = weight #定义私有变量class Student(Person): # Student继承了Person类def __init__(self,name,age,school):super().__init__(name,age) 
# 定义子类的构造方法时先要调用父类的构造方法,初始化父类实例变量self.school = school

 重写方法

       如果子类方法名与父类方法名相同,而且参数列表也相同,只是方法体不同,那么子类重写了父类的构造方法;此时在用子类对象调用该方法时,会执行子类的该方法,而不是父类的。

示例代码如下

class Animal:def __init__(self,age,sex=1,weight=0.0):self.age = ageself.sex = sexself.weight = weight #定义私有变量def eat(self):self.weight += 0.1print("动物吃...")class Dog(Animal):def eat(self):self.weight += 0.1print("狗狗吃...")a1 = Dog(2,0,10.0)
a1.eat()

代码运行结果

狗狗吃...

多继承

       多继承就是一个子类继承多个父类,在Java中只能单继承,因为多继承会发生方法冲突。Python支持多继承,当子类实例调用一个方法时,先从子类中查找,如果没有则查找父类,查找父类的顺序是按照子类声明的父类列表从左到右查找,若没有则查找父类的父类。

示例代码如下

class ParentClass1:def run(self):print('ParentClass1 run...')class ParentClass2:def run(self):print('ParentClass2 run...')class SubClass1(ParentClass1,ParentClass2):passclass SubClass2(ParentClass2,ParentClass1):passclass SubClass3(ParentClass1,ParentClass2):def run(self):print('SubClass3 run...')sub1 = SubClass1()
sub1.run()
sub2 = SubClass2()
sub2.run()
sub3 = SubClass3()
sub3.run()

代码运行结果

ParentClass1 run...
ParentClass2 run...
SubClass3 run...

示例二:加深对多继承的理解

示例代码如下

# 在多继承时需要调用多个父类的方法进行初始化时,此处定义了A、B两个父类
class FatherA():def __init__(self,name):self.name = namedef showA(self):print('我是FatherA中的方法')
class FatherB():def __init__(self,age):self.age = agedef showB(self):print('我是FatherB中的方法')
class Son(FatherA,FatherB):def __init__(self,name,age,gender):# 此时不能再用super()来调用,因为无法区分哪个父类,因此需要直接指明调用哪个父类FatherA.__init__(self,name)FatherB.__init__(self,age)self.gender = genderson = Son('CSDN@邂逅自己',18,'female')
son.showA()
son.showB()

代码运行结果

我是FatherA中的方法
我是FatherB中的方法Process finished with exit code 0

多态性

 发生多态有两个前提条件:

  1. 继承—多态发生一定是子类和父类之间
  2. 重写—子类重写了父类的方法

示例代码如下

class Figure:def draw(self):print("Figure draw...")class Ellipse(Figure):def draw(self):print("Ellipse draw...")class Triangle(Figure):def draw(self):print("Triangle draw...")f2 = Ellipse()
f2.draw()
f3 = Triangle()
f3.draw()

代码运行结果 

Ellipse draw...
Triangle draw...

案例二:加深对多态的理解

示例代码如下

# 多态可以在程序运行过程中动态决定调用哪个方法
# 该代码说明了在Python中要实现多态不需要对象之间有继承关系、也不关心对象的数据类型
# 需要有同名方法,即便在一开始不知道数据类型
class Person():def show(self):print('我是Person的show方法')
class Dog():def show(self):print('我是Dog的show方法')
class Cat():def show(self):print('我是Cat的show方法')
def fun(obj):obj.show()
person = Person()
dog = Dog()
cat = Cat()fun(person)
fun(dog)
fun(cat)

代码运行结果

我是Person的show方法
我是Dog的show方法
我是Cat的show方法Process finished with exit code 0

 类型检查

       与Java语言相比,多态性对于动态语言Python而言意义不大。但是无论多态性对Python的多大,Python作为面向对象的语言多态性是存在的。

函数描述
isinstance(object,classinfo)可以检查 object 实例是否由 classinfo 类或 infoclass 子类所创建的实例 
issubclass(class,classinfo)可以检查 class 是否是 classinfo 的子类

示例代码如下

class Figure:def draw(self):print("Figure draw...")class Ellipse(Figure):def draw(self):print("Ellipse draw...")class Triangle(Figure):def draw(self):print("Triangle draw...")
f1 = Figure()
f2 = Ellipse()
f3 = Triangle()
print(isinstance(f2,Figure)) # True
print(isinstance(f3,Figure)) # True
print(isinstance(f1,Figure)) # True
print(isinstance(f1,object)) # True,因为任何类都是 object 类的子类
print(issubclass(Figure,object)) # True
print(issubclass(Ellipse,Triangle))# False

代码运行结果

True
True
True
True
True
False

 鸭子类型

       不关注变量的类型,而是关注变量具有的方法。鸭子类型像多态一样工作,但是没有继承,只要像“鸭子”一样的行为(方法)就可以了。(用“鸭子类型”代替多态性设计能够充分发挥Python动态语言特点,但是对程序员要求也非常高)

示例代码如下

class Animal(object):def run(self):print("Animal run...")class Dog(Animal):def run(self):print("Dog run...")class Car:def run(self):print("Car run...")def go(animal):if not isinstance(animal, (Animal, Car)):  # 检查 animal 是否为 Animal 或 Car 的实例return  # 如果不是,则作为递归的基案结束递归animal.run()  # 调用 run 方法# 创建实例
animal_instance = Animal()
dog_instance = Dog()
car_instance = Car()# 使用实例调用 go 函数
go(animal_instance)
go(dog_instance)
go(car_instance)
#添加了一个类型检查来确保只有 Animal 或 Car 的实例才会调用 run 方法。如果传入的不是这些类的实例,#go 函数将直接返回,避免递归调用。

代码运行结果

Animal run...
Dog run...
Car run...

Python根类—object

Python所有类都直接或间接的继承 object 类,因此所有类拥有object类的属性和方法;object类有很多方法。

方法描述
__str__()默认返回对象的类名,以及内存地址等信息;可以重写该方法,输出描述信息
__eq__()重写该方法指定相等的规则(即指定比较的是哪些实例变量相等)
__new__()由系统调用,用于创建对象
__init__()创建对象时手动调用,用于初始化对象属性值

 示例代码1如下

class Person:def __init__(self,name,age):self.name = nameself.age = agedef __str__(self): # 重写str方法,返回什么样的字符串可以自己指定template = 'Person[name={0},age={1}]'s = template.format(self.name,self.age)return s
Person = Person('Tony',18)
print(Person)

代码运行结果

Person[name=Tony,age=18]

示例代码2如下

class Person:def __init__(self,name,age):self.name = nameself.age = agedef __str__(self): # 重写str方法,返回什么样的字符串可以自己指定template = 'Person[name={0},age={1}]'s = template.format(self.name,self.age)return sdef __eq__(self,other): # 重写eq方法,指定只有姓名和年龄都相等时才是Trueif self.name == other.name and self.age == other.age:return Trueelse:return Falsep1 = Person('Tony',18)
p2 = Person('Tony',18)
p3 = Person('Mary',18)
print(p1 == p2)
print(p1 == p3)

代码运行结果

True
False

示例代码3如下

class Person():def __init__(self,name,age):self.name = nameself.age = agedef __str__(self):return '我是重写str方法后输出的内容'
person = Person('CSDN@邂逅自己',18)
# 若不重写str方法
# 则输出对象时是输出内存地址<__main__.Person object at 0x000002516EDB5D90>
# 重写str方法后
# 则输出对应的内容,如果有的话
print(person)

代码运行结果

我是重写str方法后输出的内容Process finished with exit code 0

特殊方法

实际上,在Python中使用运算符时也是通过调用特殊方法来实现的

运算符特舒方法描述
+_add_()执行加法运算
-_sub_()执行减法运算
<,<=,==_It_(),_le_(),_eq_()执行比较运算
>,>=,!=_gt_(),_ge_(),_ne_()执行比较运算
*, /_mul_(),_truediv_()执行乘法运算,非整数运算
%,//_mod_(),_floordiv_()执行取余运算,整除算法
**_pow_()执行幂运算

特殊属性

特殊属性说明
obj.__dict__对象的属性字典,使用语法:对象名.__dict__
obj.__class__对象所属的类,使用语法:对象名.__class__
class.__bases__类的父类元组,使用语法:类名.__bases__
class.__base__类的父类,使用语法:类名.__base__
class.__mro__类的层次结构,使用语法:类名.__mro__
class._subclasses类的子类列表

示例代码如下

class FatherA():pass
class FatherB():pass
class son(FatherA,FatherB):def __init__(self,name,age):self.name = nameself.age = agea = FatherA()
b = FatherB()
c = son('张三',18)
print('-'*20,'__dict__','-'*20) # 输出对象的属性字典
print(a.__dict__)
print(b.__dict__)
print(c.__dict__)
print('-'*20,'__class__','-'*20) # 输出对象所属的类
print(a.__class__)
print(b.__class__)
print(c.__class__)
print('-'*20,'__bases__','-'*20) # 输出父类元组
print(FatherA.__bases__)
print(FatherB.__bases__)
print(son.__bases__)
print('-'*20,'__base__','-'*20) # 输出所属的父类
print(FatherA.__base__)
print(FatherB.__base__)
print(son.__base__) # 如果继承了多个父类,结果只显示第一个父类,要想知道所有的父类需要用__bases__
print('-'*20,'__mro__','-'*20) # 输出类的层次结构
print(FatherA.__mro__)
print(FatherB.__mro__)
print(son.__mro__)
# 输出结果解析,以 (<class '__main__.son'>, <class '__main__.FatherA'>, <class '__main__.FatherB'>, <class 'object'>) 
#     为例,其中,为分隔符 ,前面表示son类,中间的内容表示son类继承了FatherA和FatherB,最后内容白叟son类又间接继承了object类
print('-'*20,'__subclasses__','-'*20) # 输出类的子类列表
print(FatherA.__subclasses__())
print(FatherB.__subclasses__())
print(son.__subclasses__())

代码运行结果

-------------------- __dict__ --------------------
{}
{}
{'name': '张三', 'age': 18}
-------------------- __class__ --------------------
<class '__main__.FatherA'>
<class '__main__.FatherB'>
<class '__main__.son'>
-------------------- __bases__ --------------------
(<class 'object'>,)
(<class 'object'>,)
(<class '__main__.FatherA'>, <class '__main__.FatherB'>)
-------------------- __base__ --------------------
<class 'object'>
<class 'object'>
<class '__main__.FatherA'>
-------------------- __mro__ --------------------
(<class '__main__.FatherA'>, <class 'object'>)
(<class '__main__.FatherB'>, <class 'object'>)
(<class '__main__.son'>, <class '__main__.FatherA'>, <class '__main__.FatherB'>, <class 'object'>)
-------------------- __subclasses__ --------------------
[<class '__main__.son'>]
[<class '__main__.son'>]
[]Process finished with exit code 0

类的浅拷贝与深拷贝

  • 浅拷贝:拷贝时对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象。
  • 深拷贝:使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同。

示例代码如下

class CPU():pass
class Disk():pass
class computer():def __init__(self,cpu,disk):self.cpu = cpuself.disk = disk
cpu = CPU()
disk = Disk()
computer = computer(cpu,disk)
# 变量赋值
computer1 = computer
print(computer,'子对象的内存地址',computer.cpu,computer.disk)
print(computer1,'子对象的内存地址',computer1.cpu,computer1.disk)
# 类对象的拷贝
print('-'*20,'类对象的浅拷贝','-'*20)
import copy
computer2 = copy.copy(computer) # computer2是新产生的子对象,但是cpu和disk不变
print(computer2,'computer2子对象的内存地址',computer2.cpu,computer2.disk)
# 类对象的深拷贝
print('-'*20,'类对象的深拷贝','-'*20)
computer3 = copy.deepcopy(computer) # computer2、cpu和disk都是新产生的子对象
print(computer3,'computer3子对象的内存地址',computer3.cpu,computer3.disk)

代码运行结果

<__main__.computer object at 0x0000025DDC896840> 子对象的内存地址 <__main__.CPU object at 0x0000025DDC8968A0> <__main__.Disk object at 0x0000025DDC896810>
<__main__.computer object at 0x0000025DDC896840> 子对象的内存地址 <__main__.CPU object at 0x0000025DDC8968A0> <__main__.Disk object at 0x0000025DDC896810>
-------------------- 类对象的浅拷贝 --------------------
<__main__.computer object at 0x0000025DDC896990> computer2子对象的内存地址 <__main__.CPU object at 0x0000025DDC8968A0> <__main__.Disk object at 0x0000025DDC896810>
-------------------- 类对象的深拷贝 --------------------
<__main__.computer object at 0x0000025DDC8CEAE0> computer3子对象的内存地址 <__main__.CPU object at 0x0000025DDC8CEBA0> <__main__.Disk object at 0x0000025DDC8CEC00>Process finished with exit code 0

参考书籍:《python从小白到大牛》(第2版)关东升 编著 


文章创作不易,本文18000+字,为了大家能理解,写的很详细,这也让我花了很多时间。最后,如果觉得本文对大家有帮助的话,还请给我点个赞和关注,谢谢大家!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频监控管理平台LntonAIServer视频智能分析噪声检测应用场景

在视频监控系统中&#xff0c;噪声问题常常影响到视频画面的清晰度和可用性。噪声可能由多种因素引起&#xff0c;包括但不限于低光环境、摄像机传感器灵敏度过高、编码压缩失真等。LntonAIServer通过引入噪声检测功能&#xff0c;旨在帮助用户及时发现并解决视频流中的噪声问题…

linux 内核代码学习(八)

总体目标&#xff1a;由于fedora10 linux发行版中自带的linux2.6.xx内核源码规模太庞大了&#xff0c;对于想通读内核源码的爱好者来说太困难了&#xff0c;因此选择了linux2.4.20内核来进行测试&#xff08;最终是希望能够实现linux1.0内核的源码完全编译和测试&#xff09;。…

了解一下HTTP 与 HTTPS 的区别

介绍&#xff1a; HTTP是超文本传输协议。规定了客户端&#xff08;通常是浏览器&#xff09;和服务器之间如何传输超文本&#xff0c;也就是包含链接的文本。通常使用TCP【1】/IP协议来传输数据&#xff0c;默认端口为80。 HTTPS是超文本传输安全协议&#xff0c;具有CA证书。…

【RLHF】浅谈ChatGPT 等大模型中的RLHF算法

本文收录于《深入浅出讲解自然语言处理》专栏&#xff0c;此专栏聚焦于自然语言处理领域的各大经典算法&#xff0c;将持续更新&#xff0c;欢迎大家订阅&#xff01;​个人主页&#xff1a;有梦想的程序星空​个人介绍&#xff1a;小编是人工智能领域硕士&#xff0c;全栈工程…

TCP的流量控制深入理解

在理解流量控制之前我们先需要理解TCP的发送缓冲区和接收缓冲区&#xff0c;也称为套接字缓冲区。首先我们先知道缓冲区存在于哪个位置&#xff1f; 其中缓冲区存在于Socket Library层。 而我们的发送窗口和接收窗口就存在于缓冲区当中。在实现滑动窗口时则将两个指针指向缓冲区…

STM32F103调试DMA+PWM 实现占空比逐渐增加的软启效果

实现效果&#xff1a;DMAPWM 实现PWM输出时&#xff0c;从低电平到输出占空比逐渐增加再到保持高电平的效果&#xff0c;达到控制 MOS 功率开关软启的效果。 1.配置时钟 2.TIM 的 PWM 功能配置 选择、配置 TIM 注意&#xff1a;选择 TIM 支持 DMA 控制输出 PWM 功能的通道&a…

使用Unity的准备

下载Unity 下载Unity Hub Unity - 实时内容开发平台 | 3D、2D、VR & AR可视化https://unity.cn/ 创建账号或者登入账号 Unity安装 路径尽量为英文路径 登入账号 点击头像登入账号 这里已经登入 打开偏好 设置中文 添加许可证 获取免费版的即可 安装编辑器 新建项目…

mysql-PXC实现高可用

mysql8.0使用PXC实现高可用 什么是 PXC PXC 是一套 MySQL 高可用集群解决方案&#xff0c;与传统的基于主从复制模式的集群架构相比 PXC 最突出特点就是解决了诟病已久的数据复制延迟问题&#xff0c;基本上可以达到实时同步。而且节点与节点之间&#xff0c;他们相互的关系是…

PHP一站式解决方案高级房产系统小程序源码

一站式解决方案&#xff0c;高级房产系统让房产管理更轻松 &#x1f3e0;【开篇&#xff1a;告别繁琐&#xff0c;迎接高效房产管理新时代】&#x1f3e0; 你是否还在为房产管理的繁琐流程而头疼&#xff1f;从房源录入、客户咨询到合同签订、售后服务&#xff0c;每一个环节…

【CSS】如何写渐变色文字并且有打光效果

效果如上&#xff0c;其实核心除了渐变色文字的设置 background: linear-gradient(270deg, #d2a742 94%, #f6e2a7 25%, #d5ab4a 48%, #f6e2a7 82%, #d1a641 4%);color: #e8bb2c;background-clip: text;color: transparent;还有就是打光效果&#xff0c;原理其实就是两块遮罩&am…

7、关于LoFTR

7、关于LoFTR LoFTR论文链接&#xff1a;LoFTR LoFTR的提出&#xff0c;是将Transformer模型的注意力机制在特征匹配方向的应用&#xff0c;Transformer的提取特征的机制&#xff0c;在自身进行&#xff0c;本文提出可以的两张图像之间进行特征计算&#xff0c;非常适合进行特…

“弹性盒子”一维布局系统(补充)——WEB开发系列31

弹性盒子是一种一维布局方法&#xff0c;用于根据行或列排列元素。元素可以扩展以填补多余的空间&#xff0c;或者缩小以适应较小的空间&#xff0c;为容器中的子元素提供灵活的且一致的布局方式。 一、什么是弹性盒子&#xff1f; CSS 弹性盒子&#xff08;Flexible Box Layo…

提高开发效率的实用工具库VueUse

VueUse中文网&#xff1a;https://vueuse.nodejs.cn/ 使用方法 安装依赖包 npm i vueuse/core单页面使用&#xff08;useThrottleFn举例&#xff09; import { useThrottleFn } from "vueuse/core"; // 表单提交 const handleSubmit useThrottleFn(() > {// 具…

策略模式的小记

策略模式 策略模式支付系统【场景再现】硬编码完成不同的支付策略使用策略模式&#xff0c;对比不同&#xff08;1&#xff09;支付策略接口&#xff08;2&#xff09;具体的支付策略类&#xff08;3&#xff09;上下文&#xff08;4&#xff09;客户端&#xff08;5&#xff0…

python 交互模式怎么切换目录

假如要用交互界面调用一个.py文件&#xff1a; &#xff08;1&#xff09;用cmd界面定位到文件位置&#xff0c;如cd Desktop/data/ #进入desktop下data目录。 &#xff08;2&#xff09;接着打开python&#xff08;输入python&#xff09; 调用os &#xff08;1&#xff0…

Linux df命令详解,Linux查看磁盘使用情况

《网络安全自学教程》 df 一、字段解释二、显示单位三、汇总显示四、指定目录五、指定显示字段六、du和df结果不一样 df&#xff08;disk free&#xff09;命令用来查看系统磁盘空间使用情况。 参数&#xff1a; -h&#xff1a;&#xff08;可读性&#xff09;显示单位&#…

Mobile-Agent赛题分析和代码解读笔记(DataWhale AI夏令营)

前言 你好&#xff0c;我是GISer Liu&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;本文是DataWhale 2024 AI夏令营的最后一期——Mobile-Agent赛道&#xff0c;关于赛题分析和代码解读的学习文档总结&#xff1b;这边作者也会分享自己的思路&#xff1b; 本文是对原视频的…

万象奥科参展“2024 STM32全国巡回研讨会”—深圳站、广州站

9月3日-9月5日&#xff0c;万象奥科参展“2024 STM32全国巡回研讨会”— 深圳站、广州站。此次STM32研讨会将会走进全国11个城市&#xff0c;展示STM32在智能工业、无线连接、边缘人工智能、安全、图形用户界面等领域的产品解决方案及多样化应用实例&#xff0c;深入解读最新的…

指针之旅(3)—— 指针 与 数组

目录 1. 数组名的两种意义 2. 指针访问数组&#xff08;指针也能下标引用&#xff09; 3. 一维数组传参的本质 和 sizeof在函数中失效的原因 4. 指针数组 4.1 指针数组的概念 4.2 一级指针数组 4.3 一级指针数组模拟实现二维数组 5. 数组、指针 与 字符串 6. 数组指针…

微信小程序接入客服功能

前言 用户可使用小程序客服消息功能&#xff0c;与小程序的客服人员进行沟通。客服功能主要用于在小程序内 用户与客服直接沟通用&#xff0c;本篇介绍客服功能的基础开发以及进阶功能的使用&#xff0c;另外介绍多种客服的对接方式。 更多介绍请查看客服消息使用指南 客服视…