Python类和对象
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。
如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。
接下来我们先来简单的了解下面向对象的一些基本特征。
1. 面向对象编程 Object-Oriented Programming
-
什么是对象
-
对象是指现实中的物体或实体
-
-
什么是面向对象
-
把一切看成对象(实例),用各种对象之间的关系来描述事务。
-
-
对象都有什么特征
-
对象有很多属性(名词)
-
姓名, 年龄, 性别,
-
-
对象有很多行为(动作,动词)
-
学习,吃饭,睡觉,踢球, 工作
-
-
-
什么是类:
-
拥有相同属性和行为的对象分为一组,即为一个类
-
类是用来描述对象的工具,用类可以创建此类的对象(实例)
-
-
面向对象 示意
/-------> BYD E6(京A.88888) 实例,对象 车(类) \-------> BMW X5(京B.00000) 实例(对象) /-------> 小京巴(户籍号:000001) 狗(类) \-------> 导盲犬(户籍号:000002) /-------> 100 (对象) int(类) \-------> 200 (对象)
2. 类和对象的基础语法
2.1 类的定义
类是创建对象的 ”模板”。
-
数据成员:表明事物的特征。 相当于变量
-
方法成员:表明事物的功能。 相当于函数
-
通过
class
关键字定义类。 -
类的创建语句语法:
class 类名 (继承列表): 实例属性(类内的变量) 定义 实例方法(类内的函数method) 定义 类变量(class variable) 定义 类方法(@classmethod) 定义 静态方法(@staticmethod) 定义
示例代码:
class Dog: # 定义一个Dog类 pass class Person: def __init__(self, name, age): self.name = name self.age = age def introduce(self): print(f"My name is {self.name} and I am {self.age} years old.")
类的创建的说明:
-
类名必须为标识符(与变量的命名相同,建议首字母大写)
-
类名实质上就是变量,它绑定一个类
2.2 实例化对象(构造函数)
(1) 构造函数调用表达式
变量 = 类名([参数])
(2) 说明
-- 变量存储的是实例化后的对象地址。
-- 类名后面的参数按照构造方法的形参传递
-
对象是类的实例,具有类定义的属性和方法。
-
通过调用类的构造函数来创建对象。
-
每个对象都有自己的状态,但共享相同的方法定义。
示例代码:
class Dog: pass # 创建第一个实例: dog1 = Dog() print(id(dog1)) # 打印这个对象的ID # 创建第二个实例对象 dog2 = Dog() # dog2 绑定一个Dog类型的对象 print(id(dog2)) class Person: def __init__(self, name, age): self.name = name self.age = age def introduce(self): print(f"My name is {self.name} and I am {self.age} years old.") person1 = Person("Alice", 25) person2 = Person("Bob", 30)
实例说明
-
实例有自己的作用域和名字空间,可以为该实例添加实例变量(也叫属性)
-
实例可以调用类方法和实例方法
-
实例可以访问类变量和实例变量
2.3 self
大家学Python面向对象的时候,总会遇到一个让人难以理解的存在:self
这个self到底是谁啊,为什么每个类实例方法都有一个参数self,它到底有什么作用呢?
「先下结论:类实例化后,self即代表着实例(对象)本身」
想要理解self有个最简单的方法,就是你把self当做「实例(对象)的身份证」。
类比人类,人类就是一个Python类,每个个体的人代表着实例(对象),而每个人的身份证代表的Python中self,每个人可以凭借身份证去上大学、坐高铁、住酒店...(方法),而Python中的实例(对象)也可以凭着self去调用类的方法。
-
self
是类方法的第一个参数,用于引用对象本身。 -
self
不是Python关键字,但是约定俗成的命名,可以使用其他名称代替,但通常不建议。
示例代码:
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即代表着实例(对象)本身
3. 属性和方法
3.1 属性
-
每个实例可以有自己的变量,称为实例变量(也叫属性)
-
属性的使用语法
实例.属性名
-
属性的赋值规则
-
首次为属性赋值则创建此属性.
-
再次为属性赋值则改变属性的绑定关系.
-
-
作用
-
记录每个对象自身的数据
-
属性使用示例:
# 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('包子')
-
练习:
-
定义一个'人'类:
class Human: def set_info(self, name, age, address='不详'): '''此方法用来给人对象添加'姓名'、'年龄'和 '家庭住址' 属性 # 此处自己实现 def show_info(self): '''此处显示此人的信息''' # 此处自己实现 # 如: s1 = Human() s1.set_info('小张', 20, '北京市朝阳区') s2 = Human() s2.set_info('小李', 18)`` s1.show_info() # 小张 今年 20 岁, 家庭住址: 北京市朝阳区 s2.show_info() # 小李 今年 18 岁, 家庭住址: 不详
-
3.2 方法
class 类名(继承列表): def 实例方法名(self, 参数1, 参数2, ...): "文档字符串" 语句块
-
实例方法的作用
-
用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
-
实例方法说明
-
实例方法的实质是函数,是定义在类内的函数
-
实例方法至少有一个形参,第一个形参绑定调用这个方法的实例,一般命名为"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类的文档信息
3.3 类属性
-
类属性是类的属性,此属性属于类,不属于此类的实例
-
作用:
-
通常用来存储该类创建的对象的共有属性
-
-
类属性说明
-
类属性,可以通过该类直接访问
-
类属性,可以通过类的实例直接访问
-
-
类属性示例
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)
3.4 类方法
-
类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象
-
说明
-
类方法需要使用@classmethod装饰器定义
-
类方法至少有一个形参,第一个形参用于绑定类,约定写为'cls'
-
类和该类的实例都可以调用类方法
-
类方法不能访问此类创建的对象的实例属性
-
-
类方法示例1
class A: v = 0 @classmethod def set_v(cls, value): # def fun01(self) cls.v = value @classmethod def get_v(cls): return cls.v print(A.get_v()) A.set_v(100) print(A.get_v()) a = A() print(a.get_v())
-
类方法示例2
class MyClass: class_attr = 0 # 类属性 def __init__(self, value): self.instance_attr = value # 实例属性 @classmethod def modify_class_attr(cls, new_value): cls.class_attr = new_value print(f"类属性已修改为: {cls.class_attr}") @classmethod def try_modify_instance_attr(cls): try: cls.instance_attr = 10 # 尝试修改实例属性(会失败) except AttributeError as e: print(f"错误: {e}") def show_attrs(self): print(f"实例属性: {self.instance_attr}") print(f"类属性: {self.class_attr}") # 创建类的实例 obj = MyClass(5) # 调用类方法修改类属性 MyClass.modify_class_attr(20) # 输出: 类属性已修改为: 20 obj.show_attrs() # 输出: # 实例属性: 5 # 类属性: 20 # 调用类方法尝试修改实例属性 MyClass.try_modify_instance_attr() # 输出: 错误: type object 'MyClass' has no attribute 'instance_attr' # 尝试调用类方法修改实例属性 obj.try_modify_instance_attr() # 输出: 错误: type object 'MyClass' has no attribute 'instance_attr'
cls
在Python中,cls
是一个约定俗成的名称,用于表示类本身。在类方法(使用 @classmethod
装饰的方法)中,cls
作为第一个参数传递给方法。这使得类方法可以访问和修改类属性以及调用其他类方法,而不需要引用具体的实例。
cls
的作用
-
访问类属性:类方法可以通过
cls
访问和修改类属性。 -
调用类方法:类方法可以通过
cls
调用其他类方法。 -
创建类实例:类方法可以使用
cls
来创建类的实例。
示例
class MyClass: class_attr = 0 # 类属性 def __init__(self, value): self.instance_attr = value # 实例属性 @classmethod def modify_class_attr(cls, new_value): cls.class_attr = new_value print(f"类属性已修改为: {cls.class_attr}") @classmethod def show_class_attr(cls): print(f"类属性当前值: {cls.class_attr}") @classmethod def create_instance(cls, value): # 使用 cls 创建类实例 return cls(value) # 调用类方法修改类属性 MyClass.modify_class_attr(20) # 输出: 类属性已修改为: 20 # 调用类方法显示类属性 MyClass.show_class_attr() # 输出: 类属性当前值: 20 # 使用类方法创建类的实例 new_instance = MyClass.create_instance(10) print(f"新实例的实例属性: {new_instance.instance_attr}") # 输出: 新实例的实例属性: 10 print(f"新实例的类属性: {new_instance.class_attr}") # 输出: 新实例的类属性: 20
3.5 静态方法 @staticmethod
-
静态方法是定义在类的内部函数,此函数的作用域是类的内部
-
说明
-
静态方法需要使用@staticmethod装饰器定义
-
静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
-
静态方法只能凭借该类或类创建的实例调用
-
静态方法可以访问类属性但是不能访问实例属性
-
-
静态方法示例
class A: class_attr = 42 # 类属性 def __init__(self, value): self.instance_attr = value # 实例属性 @staticmethod def myadd(a, b): # 只能访问传递的参数,不能访问类属性和实例属性 return a + b # 创建类实例 a = A(10) # 调用静态方法 print(A.myadd(100, 200)) # 输出: 300 print(a.myadd(300, 400)) # 输出: 700 # 尝试在静态方法内访问类属性或实例属性(会导致错误) class B: class_attr = 42 def __init__(self, value): self.instance_attr = value @staticmethod def myadd(a, b): # return a + b + B.class_attr # 以下访问会导致错误 # return a + b + self.instance_attr return a + b # 创建类实例 b = B(10) # 调用静态方法 print(B.myadd(100, 200)) # 输出: 300 print(b.myadd(300, 400)) # 输出: 700
课堂练习
list01 = [ ["00","01","02","03","04"], ["10","11","12","13","04"], ["20","21","22","23","34"], ["30","31","32","33","34"], ] # 需求:30位置上向右获取3个元素 用面向对象的思想
3.6 初始化方法
-
初始化方法的作用:
对新创建的对象添加属性
-
初始化方法的语法格式:
class 类名(继承列表): def __init__(self[, 形参列表]): 语句块 # [] 代表其中的内容可省略
-
初始化方法的说明:
-
初始化方法名必须为
__init__
不可改变 -
初始化方法会在构造函数创建实例后自动调用,且将实例自身通过第一个参数self传入
__init__
方法 -
构造函数的实参将通过
__init__
方法的参数列表传入到__init__
方法中 -
初始化方法内如果需要return语句返回,则必须返回None
-
-
初始化方法示例:
# file : init_method.py class Car: def __init__(self, c, b, m): self.color = c # 颜色 self.brand = b # 品牌 self.model = m # 型号 def run(self, speed): print(self.color, "的", self.brand, self.model, "正在以", speed, "公里/小时的速度行驶") def change_color(self, c): self.color = c a4 = Car("红色", "奥迪", "A4") a4.run(199) a4.change_color("黑色") a4.run(230)
3.7 魔术方法
Python中的魔术方法(Magic Methods)是一种特殊的方法,它们以双下划线开头和结尾,例如__init__
,__str__
,__add__
等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。
以下是一些常用的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)
是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。
一些参考代码
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. 继承/派生
-
什么是继承/派生
-
继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。
-
派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为
-
-
为什么继承/派生
-
继承的目的是延续旧的类的功能
-
派生的目地是在旧类的基础上添加新的功能
-
-
继承/派生的作用
-
用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。
-
在不改变基类的代码的基础上改变原有类的功能
-
-
继承/派生名词:
-
基类(base class)/超类(super class)/父类(father class)
-
派生类(derived class)/子类(child class)
-
4.1 单继承
-
单继承的语法:
class 类名(基类名): 语句块
单继承说明:
-
单继承是指派生类由一个基类衍生出来的
-
单继承的示例1:
class Human: # 人类的共性 def say(self, what): # 说话 print("说:", what) def walk(self, distance): # 走路 print("走了", distance, "公里") class Student(Human): def study(self, subject): # 学习 print("学习:", subject) class Teacher(Human): def teach(self, language): print("教:", language) h1 = Human() h1.say("天气真好!") h1.walk(5) s1 = Student() s1.walk(4) s1.say("感觉有点累") s1.study("python") t1 = Teacher() t1.teach("面向对象") t1.walk(6) t1.say("一会吃点什么好呢")
4.2 多继承
Python支持多继承形式。多继承的类定义形如下例:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
#类定义 class people: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部无法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age)) #单继承示例 class student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 people.__init__(self,n,a,w) self.grade = g #覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) #另一个类,多继承之前的准备 class speaker(): topic = '' name = '' def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic)) #多继承 class sample(speaker,student): a ='' def __init__(self,n,a,w,g,t): student.__init__(self,n,a,w,g) speaker.__init__(self,n,t) test = sample("Tim",25,80,4,"Python") test.speak() #方法名同,默认调用的是在括号中参数位置排前父类的方法
4.3 覆盖 override
-
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖
-
作用:
-
实现和父类同名,但功能不同的方法
-
-
覆盖示例
class A: def work(self): print("A.work 被调用!") class B(A): '''B类继承自A类''' def work(self): print("B.work 被调用!!!") pass b = B() b.work() # 请问调用谁? B a = A() a.work() # 请问调用谁? A