Python类和对象
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。
如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。
接下来我们先来简单的了解下面向对象的一些基本特征。
面向对象编程 Object-Oriented Programming
1.什么是对象:对象是指现实中的物体或实体
2.什么是面向对象:把一切看成对象(实例),用各种对象之间的关系来描述事务。
3.对象都有什么特征
(1)对象有很多属性(名词)
- 姓名, 年龄, 性别,
(2)对象有很多行为(动作,动词)
- 学习,吃饭,睡觉,踢球, 工作
4.什么是类:
- 拥有相同属性和行为的对象分为一组,即为一个类
- 类是用来描述对象的工具,用类可以创建此类的对象(实例)
- 面向对象 示意
/-------> BYD E6(京A.88888) 实例,对象 车(类) \-------> BMW X5(京B.00000) 实例(对象) /-------> 小京巴(户籍号:000001) 狗(类) \-------> 导盲犬(户籍号:000002) /-------> 100 (对象) int(类) \-------> 200 (对象)
类和对象的基础语法
类的定义
1.类是创建对象的 ”模板”。
- 数据成员:表明事物的特征。 相当于变量
- 方法成员:表明事物的功能。 相当于函数
- 通过
class
关键字定义类。 - 类的创建语句语法:
class 类名 (继承列表): 实例属性(类内的变量) 定义 实例方法(类内的函数method) 定义 类变量(class variable) 定义 类方法(@classmethod) 定义 静态方法(@staticmethod) 定义
2.示例代码:
"""
python类和对象
"""
# 定义类
class Dog():
a = 10
name = "旺财变量"
def eat(self):
print("吃东西")
dog1=Dog()
print(dog1)
print(dog1.name)#旺财变量 调用类中的变量
print(dog1.eat())#吃东西 None(无返回值) 调用类中的方法
dog2 = Dog()#第二个实例化对象,不同存储地址
3.类的创建的说明:
- 类名必须为标识符(与变量的命名相同,建议首字母大写)
- 类名实质上就是变量,它绑定一个类
实例化对象(构造函数)
1.构造函数调用表达式
变量 = 类名([参数])
2. 说明
(1)变量存储的是实例化后的对象地址。
(2)类名后面的参数按照构造方法的形参传递
- 对象是类的实例,具有类定义的属性和方法。
- 通过调用类的构造函数来创建对象。
- 每个对象都有自己的状态,但共享相同的方法定义。
3.示例代码
"""
实列方法 def name(self)
实力属性 实列.属性
"""
class Dog:
#十实列属性
def __init__(self, name):
self.name=name
#实列方法
def sleep(self):
print(f"{self.name}正在睡觉")
def play(self,tool):
print(f"正在玩{tool}")
dog011=Dog("旺财")
dog011.sleep()
dog011.play("足球")
"""
创建手机 属性 苹果 华为 价格
方法 打电话 存号码
"""
class Phone:
def __init__(self,brand,price):
self.brand=brand
self.price=price
def call(self,phone):
# print(f"你正在拨打号码为{phone}的电话")
return f"你正在拨打号码为{phone}的电话"
def save(self,phone):
# print(f"你正在保存号码为{phone}的电话")
return f"你正在保存号码为{phone}的电话"
phone01=Phone("苹果","5000")
print(phone01.brand,phone01.price)
print(phone01.call(12394939329))
print(phone01.save(12384829382))
4.实例说明
- 实例有自己的作用域和名字空间,可以为该实例添加实例变量(也叫属性)
- 实例可以调用类方法和实例方法
- 实例可以访问类变量和实例变量
self
1.大家学Python面向对象的时候,总会遇到一个让人难以理解的存在:self
这个self到底是谁啊,为什么每个类实例方法都有一个参数self,它到底有什么作用呢?
「先下结论:类实例化后,self即代表着实例(对象)本身」
想要理解self有个最简单的方法,就是你把self当做「实例(对象)的身份证」。
类比人类,人类就是一个Python类,每个个体的人代表着实例(对象),而每个人的身份证代表的Python中self,每个人可以凭借身份证去上大学、坐高铁、住酒店...(方法),而Python中的实例(对象)也可以凭着self去调用类的方法。
-
self
是类方法的第一个参数,用于引用对象本身。 -
self
不是Python关键字,但是约定俗成的命名,可以使用其他名称代替,但通常不建议。
2.示例代码:
class Students:
# 构造方法
def __init__(self,name):
self.name = name
# 实例方法
def study(self,examination_results):
self.examination_results = examination_results
print("同学{}的考试分数是{}".format(self.name,self.examination_results))
print("该实例对象的地址是{}".format(self))
-
先来个实例student_a
studend_a = Students('studend_a')
print(studend_a.name)
-
再来个实例student_b
studend_b = Students('studend_b')
print(studend_b.name)
可以看出,实例(对象)不一样,打印出的结果也不一样,当类被实例化后,self.name其实就等于实例(对象).name
还是以刚刚的代码为例,我们再来调用里面的实例方法,里面会打印出self,就能看得更加明显了
实例student_a:
studend_a.study(80)
实例student_b:
studend_b.study(80)
大家能清楚看到两个实例打印出的self是不一样的,因为self代表着实例(对象)本身。
打印self出现下面对象信息
<main.Students object at 0x00000129EB0F6B38>
<main.Students object at 0x00000129EB0F6B00>
这个时候是不是就清楚了,类实例化后,self即代表着实例(对象)本身
属性和方法
属性
- 每个实例可以有自己的变量,称为实例变量(也叫属性)
- 属性的使用语法
实例.属性名
2.属性的赋值规则
- 首次为属性赋值则创建此属性.
- 再次为属性赋值则改变属性的绑定关系.
- 作用
- 记录每个对象自身的数据
- 属性使用示例:
# file : attribute.py
class Dog:
def eat(self, food):
print(self.color, '的', self.kinds, '正在吃', food)
pass
# 创建一个实例:
dog1 = Dog()
dog1.kinds = "京巴" # 添加属性
dog1.color = "白色"
dog1.color = "黄色" # 改变属性的绑定关系
print(dog1.color, '的', dog1.kinds)
dog2 = Dog()
dog2.kinds = "藏獒"
dog2.color = "棕色"
print(dog2.color, '的', dog2.kinds)
-
实例方法和实例属性(实例变量)结合在一起用:
class Dog:
def eat(self, food):
print(self.color, '的',
self.kinds, '正在吃', food)
# 创建第一个对象
dog1 = Dog()
dog1.kinds = '京巴' # 添加属性kinds
dog1.color = '白色' # 添加属性color
# print(dog1.color, '的', dog1.kinds) # 访问属性
dog1.eat("骨头")
dog2 = Dog()
dog2.kinds = '牧羊犬'
dog2.color = '灰色'
# print(dog2.color, '的', dog2.kinds) # 访问属性
dog2.eat('包子')
方法
1.格式
class 类名(继承列表): def 实例方法名(self, 参数1, 参数2, ...): "文档字符串" 语句块
2.实例方法的作用:用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
3.实例方法说明
- 实例方法的实质是函数,是定义在类内的函数
- 实例方法至少有一个形参,第一个形参绑定调用这个方法的实例,一般命名为"self"
- 实例方法名是类属性
- 实例方法的调用语法
实例.实例方法名(调用传参)
# 或
类名.实例方法名(实例, 调用传参)
带有实例方法的简单的Dog类
# file: instance_method.py
class Dog:
"""这是一个种小动物的定义
这种动物是狗(犬)类,用于创建各种各样的小狗
"""
def eat(self, food):
'''此方法用来描述小狗吃东西的行为'''
print("小狗正在吃", food)
def sleep(self, hour):
print("小狗睡了", hour, "小时!")
def play(self, obj):
print("小狗正在玩", obj)
dog1 = Dog()
dog1.eat("骨头")
dog1.sleep(1)
dog1.play('球')
dog2 = Dog()
dog2.eat("窝头")
dog2.sleep(2)
dog2.play('飞盘')
>>> help(Dog) # 可以看到Dog类的文档信息
类属性
1.类属性是类的属性,此属性属于类,不属于此类的实例
2.作用:通常用来存储该类创建的对象的共有属性
3.类属性说明
- 类属性,可以通过该类直接访问
- 类属性,可以通过类的实例直接访问
4.类属性示例
class Human:
total_count = 0 # 创建类属性 self.name = name
def __init__(self, name):
self.name = name
print(Human.total_count)
h1 = Human("小张")
print(h1.total_count)
类方法
1.类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象
2.说明
- 类方法需要使用@classmethod装饰器定义
- 类方法至少有一个形参,第一个形参用于绑定类,约定写为'cls'
- 类和该类的实例都可以调用类方法
- 类方法不能访问此类创建的对象的实例属性
2.类方法和类属性示例
"""
类属性和类方法 实例
"""
class Myclass:
class_attr = 0 #类属性
def __init__(self, name):
self.name = name #实例属性
@classmethod
def modify_class(cls,num):
cls.class_attr = num
# print(self.name) 出错,类方法不能访问实例书写
print(f"类属性已经修改为:{cls.class_attr}")
myclass01 = Myclass("小红")
myclass01.modify_class(100)
Myclass.modify_class(200)
print(myclass01.class_attr)
print(Myclass.class_attr)
3.练习1
"""
类方法 类属性
工商银行总行 all_price
分行 拿钱
"""
class ICBC:
all_price = 100000
@classmethod
def print_price(cls):
print(f"总行还剩余{cls.all_price}")
def __init__(self,name,money):
self.name = name
self.money = money
# 总行的前减少
ICBC.all_price -=money
i01 = ICBC("天运支行",10000)
i01 = ICBC("陶然支行",10000)
ICBC.print_price()
4.练习2
"""
计数器
统计当前类创建了多少个实例对象 类属性、类方法、实例属性、实例方法
"""
class Count:
count = 0
@classmethod
def count_object(cls):
print(f"当前一共创建了{cls.count}个对象")
def __init__(self,name):
self.name = name
Count.count += 1
count01 = Count("对象1")
count02 = Count("对象2")
count03 = Count("对象3")
count04 = Count("对象4")
Count.count_object()
5.练习4的改进
"""
计数器
改进
统计当前类创建了多少个实例对象 类属性、类方法、实例属性、实例方法
统计实例方法被调用了几次,并且调用的是什么实例方法
创建对象和实力方法一共调用了几次
"""
class Count:
createcount = 0
methodcount = 0
@classmethod
def count_object(cls):
print(f"当前一共创建了{cls.createcount}次对象")
print(f"当前一一共调用了{cls.methodcount}次实例化方法")
def __init__(self,name):
self.name = name
Count.createcount += 1
print(f"当前Count类的实例对象被创建{Count.createcount}次")
def fun01(self):
fun01count = 0
fun01count +=1
print(f"当前fun01实例化方法被调用{fun01count}次")
Count.methodcount +=1
def fun02(self):
fun02count = 0
fun02count +=1
print(f"当前fun02实例化方法被调用{fun02count}次")
Count.methodcount +=1
count01 = Count("对象1")
count02 = Count("对象2")
count03 = Count("对象3")
count04 = Count("对象4")
count01.fun01()
count01.fun02()
Count.count_object()
cls
1.在Python中,cls
是一个约定俗成的名称,用于表示类本身。在类方法(使用 @classmethod
装饰的方法)中,cls
作为第一个参数传递给方法。这使得类方法可以访问和修改类属性以及调用其他类方法,而不需要引用具体的实例。
2.cls
的作用
-
访问类属性:类方法可以通过
cls
访问和修改类属性。 -
调用类方法:类方法可以通过
cls
调用其他类方法。 -
创建类实例:类方法可以使用
cls
来创建类的实例。
静态方法 @staticmethod
1.静态方法是定义在类的内部函数,此函数的作用域是类的内部
2.说明
- 静态方法需要使用@staticmethod装饰器定义
- 静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
- 静态方法只能凭借该类或类创建的实例调用
- 静态方法可以访问类属性但是不能访问实例属性
3.静态方法示例
"""
静态方法的使用案例
"""
class Myclass:
count = 0 # 类属性
def __init__(self,name):
self.name = name # 实例属性
@staticmethod
def myadd(x,y):
print(Myclass.count)
# print(self.name)#无法访问实例属性
# return x+y #静态函数为返回值
print(f"x+y={x+y}")
my01 = Myclass("张三")
# 实例对象调用
my01.myadd(2,3) # 0 x+y=5 这里的0是类属性的值,因为此时类属性没有相关操作,还是初始化时的值0
# 类名进行调用
Myclass.myadd(3,5) #同理
初始化方法
1.初始化方法的作用:对新创建的对象添加属性
2.初始化方法的语法格式:
class 类名(继承列表): def __init__(self[, 形参列表]): 语句块 # [] 代表其中的内容可省略
3.初始化方法的说明:
- 初始化方法名必须为
__init__
不可改变 - 初始化方法会在构造函数创建实例后自动调用,且将实例自身通过第一个参数self传入
__init__
方法 - 构造函数的实参将通过
__init__
方法的参数列表传入到__init__
方法中 - 初始化方法内如果需要return语句返回,则必须返回None
4.初始化方法示例:
"""
self
初始化方法__init__
"""
class Students:
def __init__(self,name,age,sex):
#实例化属性
self.name = name
self.age = age
self.sex = sex
# print(self)
student1=Students("李华",18,"男")
# s=student1.__init__("李华",18,"男")
# print(s)#有无print(self),返回None
print(student1.name)#李华
# print(student1.name)#有无print(self),访问属性的方法一样
student1.name="李明"
print(student1.name)#李明
魔术方法
1.Python中的魔术方法(Magic Methods)是一种特殊的方法,它们以双下划线开头和结尾,例如__init__
,__str__
,__add__
等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。
2.以下是一些常用的Python魔术方法:
-
__init__(self, ...)
: 初始化对象,通常用于设置对象的属性。 -
__str__(self)
: 定义对象的字符串表示形式,可通过str(object)
或print(object)
调用。例如,您可以返回一个字符串,描述对象的属性。 -
__repr__(self)
: 定义对象的“官方”字符串表示形式,通常用于调试。可通过repr(object)
调用。 -
__len__(self)
: 定义对象的长度,可通过len(object)
调用。通常在自定义容器类中使用。 -
__getitem__(self, key)
: 定义对象的索引操作,使对象可被像列表或字典一样索引。例如,object[key]
。 -
__setitem__(self, key, value)
: 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如,object[key] = value
。 -
__delitem__(self, key)
: 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如,del object[key]
。 -
__iter__(self)
: 定义迭代器,使对象可迭代,可用于for
循环。 -
__next__(self)
: 定义迭代器的下一个元素,通常与__iter__
一起使用。 -
__add__(self, other)
: 定义对象相加的行为,使对象可以使用+
运算符相加。例如,object1 + object2
。 -
__sub__(self, other)
: 定义对象相减的行为,使对象可以使用-
运算符相减。 -
__eq__(self, other)
: 定义对象相等性的行为,使对象可以使用==
运算符比较。 -
__lt__(self, other)
: 定义对象小于其他对象的行为,使对象可以使用<
运算符比较。 -
__gt__(self, other)
: 定义对象大于其他对象的行为,使对象可以使用>
运算符比较。 -
__call__(self, other)
是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。
3.一些参考代码
- 1.
__init__(self, ...)
: 初始化对象
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
- 2.
__str__(self)
: 字符串表示形式
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
return f"MyClass instance with value: {self.value}"
obj = MyClass(42)
print(obj) # 输出:MyClass instance with value: 42
- 3.
__repr__(self)
: 官方字符串表示形式
class MyClass:
def __init__(self, value):
self.value = value
def __repr__(self):
return f"MyClass({self.value})"
#def __repr__(self):
#return f"Person(name={self.name}, age={self.age})"
obj = MyClass(42)
print(obj) # 输出:MyClass(42)
- 4.
__len__(self)
: 定义对象的长度
class MyList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
my_list = MyList([1, 2, 3, 4])
print(len(my_list)) # 输出:4
- 5.
__getitem__(self, key)
: 索引操作
class MyDict:
def __init__(self):
self.data = {}
def __getitem__(self, key):
return self.data.get(key)
my_dict = MyDict()
my_dict.data = {'key1': 'value1', 'key2': 'value2'}
print(my_dict['key1']) # 输出:value1
- 6.
__setitem__(self, key, value)
: 赋值操作
class MyDict:
def __init__(self):
self.data = {}
def __setitem__(self, key, value):
self.data[key] = value
my_dict = MyDict()
my_dict['key1'] = 'value1'
print(my_dict.data) # 输出:{'key1': 'value1'}
- 7.
__delitem__(self, key)
: 删除操作
class MyDict:
def __init__(self):
self.data = {}
def __delitem__(self, key):
del self.data[key]
my_dict = MyDict()
my_dict.data = {'key1': 'value1', 'key2': 'value2'}
del my_dict['key2']
print(my_dict.data) # 输出:{'key1': 'value1'}
- 8.
__iter__(self)
: 迭代器
class MyIterable:
def __init__(self):
self.data = [1, 2, 3, 4]
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
my_iterable = MyIterable()
for item in my_iterable:
print(item)
# 输出:1, 2, 3, 4
-
9.__call__(self, *args, **kwargs)
class MyCallable:
def __call__(self, *args, **kwargs):
print("Called with arguments:", args, kwargs)
# 创建类的实例
my_callable = MyCallable()
# 像调用函数一样调用实例
my_callable(1, 2, 3, key="value")
4.例
"""
方法的重写(魔方方法)
"""
# a + b
# def __add__(self,other)的自调用
class Myclass:
def __init__(self,x,y):
self.x = x
self.y = y
def __add__(self, other):
return self.x + other.x,self.y + other.y
def __str__(self):
return "Myclass"#返回一句话
# return "x = %d , y = %d," % (self.x, self.y) #返回实例初始化属性
d01 = Myclass(10,20)
d02 = Myclass(30,40)
print(d01+d02)#报错
# 重写后
d01 = Myclass(10,20)
d02 = Myclass(30,40)
print(d01+d02)
"""
在Python中,要支持多个对象的连续加法(如 d01 + d02 + d03),你需要确保 __add__ 方法返回的结果仍然是一个可以与 Myclass 实例相加的类型。在你的例子中,__add__ 方法返回的是一个元组 (self.x + other.x, self.y + other.y),而不是一个 Myclass 实例。因此,当你尝试执行 d01 + d02 + d03 时,Python会首先计算 d01 + d02(得到一个元组),然后尝试将这个元组与 d03 相加,这会引发一个 TypeError,因为元组不能与 Myclass 实例相加。
为了解决这个问题,你可以让 __add__ 方法返回一个新的 Myclass 实例,而不是一个元组。这样,每次加法操作都会返回一个新的 Myclass 实例,你可以继续对它进行加法操作。
"""
class Myclass:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
# 确保 other 也是一个 Myclass 实例
if not isinstance(other, Myclass):
raise TypeError("Unsupported operand type(s) for +: 'Myclass' and '{}'".format(type(other).__name__))
# 返回一个新的 Myclass 实例,其属性是加法运算的结果
return Myclass(self.x + other.x, self.y + other.y)
def __str__(self):
# 返回实例的字符串表示
return f"Myclass(x={self.x}, y={self.y})"
# 创建 Myclass 实例
d01 = Myclass(10, 20)
d02 = Myclass(30, 40)
d03 = Myclass(50, 60)
# 执行连续加法操作
result = d01 + d02 + d03
# 输出结果
print(result) # 输出: Myclass(x=90, y=120)
class Myclass:
def __init__(self,x,y,item):
self.x = x
self.y = y
self.item = item
def __len__(self):
return len(self.item)
data = [1,2,3]
d01 = Myclass(10,2,data)
print(len(d01))
# __add__ 方法现在返回一个新的 Myclass 实例,其 x 和 y 属性分别是两个相加实例对应属性的和。此外,__str__ 方法也被修改以提供更详细的字符串表示,包括 x 和 y 的值。这样,当你执行 print(d01 + d02 + d03) 时,它会输出一个表示加法结果的 Myclass 实例的字符串。
继承/派生
"""
继承的使用案例
"""
#父类 基类
class Hunman:
def __init__(self,name,age):
self.name = name
self.age = age
def say(self,what):
print(f"说了{what}")
def walk(self,step):
print(f"做了{step}")
class Teacher:
def teach(self):
print("正在教书,这个类此时为Student继承的第二个父类,所有一个类可以继承多个父类")
class Student(Hunman,Teacher):
def __init__(self,score,name,age):
Hunman.__init__(self,name,age)#调用父类的构造函数,让子类可以去调用父类的实例属性
self.score = score
def study(self,hour):
print(f"学习了{hour}小时")
def say(self):
print(f"子类覆盖父类方法成功,并打印{self.name}和{self.age}")
h01 = Hunman("张三",18)
s01 = Student(22,"张三",18)#子类父类的实例属性都需要传参
s01.walk("子类可以调用父类地实例方法")
s01.say()
print(s01.name)#子类继承了父类的实例属性,并进行调用
s01.teach()#可继承1多个父类
1.什么是继承/派生
- 继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。
- 派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为
2.为什么继承/派生
- 继承的目的是延续旧的类的功能
- 派生的目地是在旧类的基础上添加新的功能
3.继承/派生的作用
- 用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。
- 在不改变基类的代码的基础上改变原有类的功能
4.继承/派生名词:
- 基类(base class)/超类(super class)/父类(father class)
- 派生类(derived class)/子类(child class)
单继承
1.单继承的语法:
class 类名(基类名): 语句块
2.单继承说明:单继承是指派生类由一个基类衍生出来的
多继承
1.Python支持多继承形式。多继承的类定义形如下例:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
覆盖 override
1.覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖
2.作用:实现和父类同名,但功能不同的方法
封装 enclosure
1.封装是指隐藏类的实现细节,让使用者不用关心这些细节;
2.封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
3.Python的封装是假的(模拟的)封装
4.私有属性和方法
(1)python类中以双下划线(__
)开头,不以双下划线结尾的标识符为私有成员,私有成员只能使用方法来进行访问和修改
- 以
__
开头的属性为类的私有属性,在子类和类外部无法直接使用 - 以
__
开头的方法为私有方法,在子类和类外部无法直接调用 - 强行访问:实例化对象._类名._ _实列化属性(方法)
5.私有属性和方法示例:
"""
封装
"""
class A:
def __init__(self):
self.__a = 100 # 私有属性
self.b = 200 # 实例属性
#私有方法
def __m1(self):
print("私有方法m1")
a = A()
print(a.b)
# print(a.a) # 私有属性访问报错,不能在外部访问
# a.__m1() # 私有方法访问报错,不能在外部访问
print(a._A__a)#强行访问
class B(A):
def __init__(self):
A.__init__()
def m2(self):
self.__m1 # 报错
print(f"打印父类私有属性{self.__a}") # 报错
b = B()
b.m2() # 子类访问父类的私有方法访问报错,也不能再内部访问
b.__a # 子类访问父类的私有属性访问报错,也不能再内部访问
多态 polymorphic
1.什么是多态:
- 字面意思"多种状态"
- 多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
2.状态:
- 静态(编译时状态)
- 动态(运行时状态)
3.多态说明:
- 多态调用的方法与对象相关,不与类型相关
- Python的全部对象都只有"运行时状态(动态)", 没有"C++语言"里的"编译时状态(静态)"
4.多态示例:
class Shape:
def draw(self):
print("Shape的draw()被调用")
class Point(Shape):
def draw(self):
print("正在画一个点!")
class Circle(Point):
def draw(self):
print("正在画一个圆!")
def my_draw(s):
s.draw() # 此处显示出多态
shapes1 = Circle()
shapes2 = Point()
my_draw(shapes1) # 调用Circle 类中的draw
my_draw(shapes2) # Point 类中的draw
4.面向对象编程语言的特征:
- 继承
- 封装
- 多态
方法重写
- 如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法
函数重写
-
在自定义类内添加相应的方法,让自定义类创建的实例像内建对象一样进行内建函数操作
对象转字符串函数重写
-
对象转字符串函数重写方法
-
str() 函数的重载方法:
-
def __str__(self)
-
如果没有
__str__(self)
方法,则返回repr(obj)函数结果代替
-
-
-
-
str/repr函数重写示例
class MyNumber: "此类用于定义一个自定义的类,用于演示str/repr函数重写" def __init__(self, value): "构造函数,初始化MyNumber对象" self.data = value def __str__(self): "转换为普通字符串" return "%s" % self.data n1 = MyNumber("一只猫") n2 = MyNumber("一只狗") print("str(n2) ===>", str(n2))
内建函数重写
-
__abs__
abs(obj) 函数调用 -
__len__
len(obj) 函数调用 -
__reversed__
reversed(obj) 函数调用 -
__round__
round(obj) 函数调用 - 内建函数 重写示例
# file : len_overwrite.py
class MyList:
def __init__(self, iterable=()):
self.data = [x for x in iterable]
def __repr_(self):
return "MyList(%s)" % self.data
def __len__(self):
print("__len__(self) 被调用!")
return len(self.data)
def __abs__(self):
print("__len__(self) 被调用!")
return MyList((abs(x) for x in self.data))
myl = MyList([1, -2, 3, -4])
print(len(myl))
print(abs(myl))
运算符重载
1.运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作
2.运算符重载的作用
- 让自定义类的实例像内建对象一样进行运算符操作
- 让程序简洁易读
- 对自定义对象将运算符赋予新的运算规则
3.运算符重载说明:运算符重载方法的参数已经有固定的含义,不建议改变原有的意义
算术运算符重载
方法名 | 运算符和表达式 | 说明 |
---|---|---|
__add__(self, rhs) |
self + rhs | 加法 |
__sub__(self, rhs) |
self - rhs | 减法 |
__mul__(self, rhs) |
self * rhs | 乘法 |
__truediv__(self, rhs) |
self / rhs | 除法 |
__floordiv__(self, rhs) |
self // rhs | 地板除 |
__mod__(self, rhs) |
self % rhs | 取模(求余) |
__pow__(self, rhs) |
self ** rhs | 幂 |
rhs (right hand side) 右手边
1.二元运算符重载方法格式:
def __xxx__(self, other): ....
2.算术运算符重载示例
class MyNumber:
"此类用于定义一个自定义的类,用于演示运算符重载"
def __init__(self, value):
"构造函数,初始化MyNumber对象"
self.data = value
def __str__(self):
"转换为表达式字符串"
return "MyNumber(%d)" % self.data
def __add__(self, rhs):
"加号运算符重载"
print("__add__ is called")
return MyNumber(self.data + rhs.data)
def __sub__(self, rhs):
"减号运算符重载"
print("__sub__ is called")
return MyNumber(self.data - rhs.data)
n1 = MyNumber(100)
n2 = MyNumber(200)
print(n1 + n2)
print(n1 - n2)
super函数
1.super() 函数是用于调用父类(超类)的一个方法。
2.super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
3.钻石问题
"""
super
解决钻石继承问题
"""
class A:
def process(self):
print("A process")
class B(A):
def process(self):
print("B process")
A.process(self) # 手动调用A的父类的方法,不调用会被覆盖
class C(A):
def process(self):
print("C process")
A.process(self)
class D(B,C):
def process(self):
print("D process")
B.process(self)
C.process(self)
d = D()
print(d.process()) # A调用了两次,钻石继承问题
3.super() 方法的语法:
(1)在子类方法中可以使用super().add()调用父类中已被覆盖的方法
- 调用属性
"""
super函数用法
"""
class Parent:
def __init__(self,name):
self.name = name
class Child(Parent):
def __init__(self,name,age):
super().__init__(name)
self.age = age
def print_info(self):
print(self.name,self.age)
c01 = Child("李华",20)
c01.print_info()
- 调用方法
class A:
def process(self):
print("A process")
class B(A):
def process(self):
print("B process")
super().process() # 调用super()
class C(A):
def process(self):
print("C process")
super().process()
class D(B,C):
def process(self):
print("D process")
super().process()#有去重功能
d = D()
print(d.process()) # D,B,C,A
4.可以使用super(Child, obj).myMethod()用子类对象调用父类已被覆盖的方法
class A:
def add(self, x):
y = x+1
print(y)
class B(A):
def add(self, x):
print("子类方法")
super().add(x)
b = B()
b.add(2) # 3
class Parent: # 定义父类
def myMethod(self):
print ('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print ('调用子类方法')
c = Child()# 子类实例
c.myMethod()# 子类调用重写方法
super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法
super().init()
1.super().__init__()
是 Python 中用于调用父类(基类)构造函数的一种方式。它通常用于子类的构造函数中,以确保父类的构造函数被正确调用和初始化。这在继承(inheritance)中尤为重要,因为父类的初始化代码可能包含设置实例变量或执行其他重要的初始化任务。
class Parent:
def __init__(self):
print("Parent class constructor called")
self.parent_attribute = "I am a parent attribute"
class Child(Parent):
def __init__(self):
super().__init__()
print("Child class constructor called")
self.child_attribute = "I am a child attribute"
# 创建一个 Child 类的实例
child_instance = Child()
print(child_instance.parent_attribute)
# 输出
# Parent class constructor called
# Child class constructor called
2.注释
(1)Parent 类:
- 定义了一个构造函数
__init__()
,在构造函数中打印了一条消息,并初始化了一个属性parent_attribute
。
(2)Child 类:
- 继承自
Parent
类。 - 在其构造函数
__init__()
中,首先调用了super().__init__()
。这行代码会调用Parent
类的构造函数,确保Parent
类的初始化逻辑被执行。 - 然后打印了一条消息,并初始化了一个属性
child_attribute
。
(3)实例化 Child 类:
- 创建
Child
类的实例时,首先执行Parent
类的构造函数,打印 "Parent class constructor called",然后执行Child
类的构造函数,打印 "Child class constructor called"。
3.为什么使用 super().__init__()
?
- 代码重用:避免在子类中重复父类的初始化代码。
- 正确初始化:确保父类的初始化逻辑(如设置属性、分配资源等)被执行。
-
支持多重继承:在多重继承情况下,
super()
可以确保所有基类的构造函数都被正确调用。
4.举例
class Attention(nn.Module):
def __init__(self, dim, heads=8, dim_head=64, dropout=0.):
super().__init__()
inner_dim = dim_head * heads # 计算内部维度
project_out = not (heads == 1 and dim_head == dim) # 判断是否需要投影输出