七、类和对象
在现实生活中,人是抽象的类,简称人类,像张三,李四,王五,这样真真正正存在的人的实体,我们将这些实体称为对象。
1. 初识类和对象
在面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,instance),其中 类是某一批对象的抽象,可以把类理解成某种概念;对象才是一个具体存在的实体。
Python中类的基本定义:
class 类名:
零个或者多个属性
零个或者多个方法
示例:
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
# 创建一个对象 zhangsan
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.age = 21
print(f"姓名:{zhangsan.name},年龄:{zhangsan.age}")
# 在创建一个对象 lisi
lisi = Person()
lisi.name = "李四"
lisi.age = 12
print(f"姓名:{lisi.name},年龄:{lisi.age}")
执行结果:
2. 类的成员方法
类中有属性,用来记录对象数据;
类中也有方法,用来描述对象的行为;
类中的方法和前面所学函数语法一致。
类里面访问成员变量,可以用self关键字,
self表示当前的类对象
看下如下实例:
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
# 成员方法 say 打印输出姓名和年龄
def say(self):
print(f"姓名:{self.name},年龄:{self.age}")
def say2(self, msg):
print(f"姓名:{self.name},年龄:{self.age},备注:{msg}")
# 创建一个对象 zhangsan
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.age = 21
zhangsan.say()
zhangsan.say2("成年人")
# 在创建一个对象 lisi
lisi = Person()
lisi.name = "李四"
lisi.age = 12
lisi.say()
lisi.say2("未成年")
执行结果:
3. 类的构造方法
Python类里的 __init__
方法就是类的构造方法,用作构造对象,同时还可以给对象初始化属性值用。
假如类里面没有写init构造方法,默认会提供一个空的构造方法。
我们写上空的init构造方法,再写个打印语句测试下:
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self):
print("构造方法")
# 成员方法 say 打印输出姓名和年龄
def say(self):
print(f"姓名:{self.name},年龄:{self.age}")
def say2(self, msg):
print(f"姓名:{self.name},年龄:{self.age},备注:{msg}")
# 创建一个对象 zhangsan
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.age = 21
zhangsan.say()
zhangsan.say2("成年人")
# 在创建一个对象 lisi
lisi = Person()
lisi.name = "李四"
lisi.age = 12
lisi.say()
lisi.say2("未成年")
执行结果:
说明 构造对象的时候,调用了init
构造方法。
我们可以通过有参init构造方法,来初始化对象属性:
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print(f"姓名:{self.name},年龄:{self.age}")
def say2(self, msg):
print(f"姓名:{self.name},年龄:{self.age},备注:{msg}")
# 创建一个对象 zhangsan
zhangsan = Person("张三",28)
zhangsan.say()
zhangsan.say2("成年人")
# 在创建一个对象 lisi
lisi = Person("李四",12)
lisi.say()
lisi.say2("未成年")
执行结果:
4. 魔法方法
Python的魔法方法,也称为特殊方法或双下划线方法,是一种特殊的方法,用于在类中实现一些 特殊的 功能。这些方法的名称始终以双下划线开头和结尾,例如:init,repr,add等。
序号 | 魔法方法 | 解释 |
---|---|---|
1 | __init__(self[, args...]) | 构造函数,创建一个新对象时调用,其中self代表当前类的一个实例对象。 |
2 | __del__(self) | 析构函数,当对象被删除时调用。 |
3 | __repr__(self) | 定义对象的字符串表示,通过重载该方法可以自定义输出对象的字符串表示。 |
4 | __str__(self) | 定义对象的字符串表示,与__repr__ 功能相同,但是在打印对象的str()方法调用时被调用。 |
5 | __bytes__(self) | 定义对象的字节表示,用于调用bytes()内置函数时调用。 |
6 | __format__(self, f_spec) | 定义格式化字符串,与format()内置函数结合使用。 |
7 | __hash__(self) | 定义对象的哈希值,在字典、集合等数据结构中使用。 |
8 | __bool__(self) | 定义对象的布尔值,该方法必须返回True或False。 |
9 | __getattr__(self, name) | 动态返回某个属性的值。 |
10 | __setattr__(self, name, value) | 动态设置某个属性的值。 |
11 | __eq__(self, other) | 定义对象间的等于操作,当使用==比较两个对象时被调用。 |
12 | __ne__(self, other) | 定义对象间的不等于操作,当使用!=比较两个对象时被调用。 |
13 | __lt__(self, other) | 定义对象间的小于操作,当使用<比较两个对象时被调用。 |
14 | __le__(self, other) | 定义对象间的小于等于操作,当使用<=比较两个对象时被调用。 |
15 | __gt__(self, other) | 定义对象间的大于操作,当使用>比较两个对象时被调用。 |
16 | __ge__(self, other) | 定义对象间的大于等于操作,当使用>=比较两个对象时被调用。 |
17 | __add__(self, other) | 定义对象间的加法操作,当使用+运算符时被调用。 |
18 | __sub__(self, other) | 定义对象间的减法操作,当使用-运算符时被调用。 |
19 | __mul__(self, other) | 定义对象间的乘法操作,当使用*运算符时被调用。 |
20 | __truediv__(self, other) | 定义对象间的除法操作,当使用/运算符时被调用。 |
21 | __floordiv__(self, other) | 定义对象间的整除操作,当使用//运算符时被调用。 |
22 | __mod__(self, other) | 定义对象间的取模操作,当使用%运算符时被调用。 |
23 | __pow__(self, other[, modulo]) | 定义对象间的幂运算操作,当使用**运算符时被调用。 |
24 | __lshift__(self, other) | 定义对象间的左移位操作,当使用<<运算符时被调用。 |
25 | __rshift__(self, other) | 定义对象间的右移位操作,当使用>>运算符时被调用。 |
26 | __and__(self, other) | 定义对象间的位与操作,当使用&运算符时被调用。 |
27 | __or__(self, other) | 定义对象间的位或操作,当使用|运算符时被调用。 |
28 | __xor__(self, other) | 定义对象间的异或操作,当使用^运算符时被调用。 |
29 | __invert__(self) | 定义对象的按位取反操作,当使用~运算符时被调用。 |
30 | __call__(self[, args...]) | 定义可调用对象,使实例对象可以像函数一样被调用。 |
str 方法,定义对象的显示字符串,默认是对象内存地址,我们可以通过重写str方法,显示我们需要的数据。
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
zhangsan = Person("张三", 21)
print(zhangsan)
print(str(zhangsan))
执行结果:
重写str方法:
def __str__(self):
return f"姓名:{self.name},年龄:{self.age}"
eq 定义对象间的等于操作,当使用==比较两个对象时被调用。
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"姓名:{self.name},年龄:{self.age}"
def __eq__(self, other):
return self.age == other.age
zhangsan = Person("张三", 21)
# 在创建一个对象 lisi
lisi = Person("李四", 21)
print(zhangsan == lisi)
输出: True
5. 隐藏和封装
封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
Python里私有属性和方法的定义,命名以__开头(两个下划线)
私有属性和方法,外部对象无法直接访问,否则报错。 私有属性和方法只能类内部通过self访问使用。
# 定义人类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
# 私有属性 得分
__score = None
def __init__(self, name, age, score):
self.name = name
self.age = age
self.__score = score
# 成员方法 say 打印输出姓名和年龄
def say(self):
print(f"姓名:{self.name},年龄:{self.age},成绩:{self.__score}")
self.__check_score()
# 定义私有方法 不及格,叫家长来下
def __check_score(self):
if self.__score < 60:
print(f"叫{self.name}的家长来下学校")
# 创建一个对象 zhangsan
zhangsan = Person("张三", 11, 55)
# zhangsan.__check_score() # 访问私有方法 报错
# print(zhangsan.__age) # 访问私有属性 报错
# 创建一个对象 lisi
lisi = Person("李四", 11, 55)
lisi.say()
执行结果:
6. 类的继承
继承是面向对象的三大特征之一,也是实现软件复用的重要手段。Python 的继承是多继承机制 ,即一个 子类可以同时有多个直接父类。
语法:
class 子类(父类1,父类2,...):
子类继承父类的属性和方法,同时子类可以扩展新的属性和方法。
示例:
# 定义一个水果类,作为父类
class Fruit:
# 父类属性 口感
taste = None
# 父类方法 打印口感
def say_taste(self):
print(f"口感:{self.taste}")
# 定义香蕉子类,继承父类Food
class Banana(Fruit):
# 子类属性 颜色
color = None
def say_color(self):
print(f"香蕉颜色:{self.color}")
b1 = Banana()
b1.taste = "果肉芳香"
b1.say_taste()
b1.color = "黄色"
b1.say_color()
执行结果:
多继承实例:
# 定义一个水果类,作为父类
class Fruit:
# 父类属性 口感
taste = None
# 父类方法 打印口感
def say_taste(self):
print(f"口感:{self.taste}")
class Food:
# 父类属性 价格
price = None
# 父类方法 打印价格
def say_price(self):
print(f"价格:{self.price}")
# 定义香蕉子类,继承父类Food
class Banana(Fruit, Food):
# 子类属性 颜色
color = None
def say_color(self):
print(f"香蕉颜色:{self.color}")
def __str__(self):
return f"口感:{self.taste},价格:{self.price},颜色:{self.color}"
b1 = Banana()
b1.taste = "果肉芳香"
b1.say_taste()
b1.color = "黄色"
b1.say_color()
b1.price = "10"
b1.say_price()
print(b1)
执行结果:
重写父类属性和方法
当子类根据业务需求,对父类方法不满意,我们可以在子类里重写父类方法,当然属性也可以重写。
# 定义一个水果类,作为父类
class Fruit:
# 父类属性 口感
taste = None
# 父类方法 打印口感
def say_taste(self):
print(f"口感:{self.taste}")
class Food:
# 父类属性 价格
price = None
# 父类方法 打印价格
def say_price(self):
print(f"价格:{self.price}")
# 定义香蕉子类,继承父类Food
class Banana(Fruit, Food):
# 子类属性 颜色
color = None
# 重写父类属性
price = 9
def say_color(self):
print(f"香蕉颜色:{self.color}")
def __str__(self):
return f"口感:{self.taste},价格:{self.price},颜色:{self.color}"
# 重写父类方法 打印价格
def say_price(self):
print(f"香蕉价格:{self.price}")
b1 = Banana()
b1.taste = "果肉芳香"
b1.say_taste()
b1.color = "黄色"
b1.say_color()
print(b1.price)
b1.price = "10"
b1.say_price()
print(b1)
执行结果:
我们self调用的是子类继承下来的属性和方法。如果我们想调用父类的属性和方法,可以通过如下两种方式:
方式一: 父类名.父类属性或者方法(self)
方式二: super().父类属性或者方法()
# 定义一个水果类,作为父类
class Fruit:
# 父类属性 口感
taste = None
# 父类方法 打印口感
def say_taste(self):
print(f"口感:{self.taste}")
class Food:
# 父类属性 价格
price = 8
# 父类方法 打印价格
def say_price(self):
print(f"价格:{self.price}")
# 定义香蕉子类,继承父类Food
class Banana(Fruit, Food):
# 子类属性 颜色
color = None
# 重写父类属性
price = 9
def say_color(self):
print(f"香蕉颜色:{self.color}")
# 重写父类方法 打印价格
def say_price(self):
print(f"父类属性打印:{super().price},{Food.price}")
# 父类方法调用
super().say_price()
Food.say_price(self)
print(f"香蕉价格:{self.price}")
b1 = Banana()
b1.say_price()
执行结果:
父类属性打印:8,8
价格:9
价格:9
香蕉价格:9
7. 多态
多态,是指:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。 通过继承 和重写 实现。
class Animal:
def say(self):
pass
class Cat(Animal):
def say(self):
print("喵喵")
class Dog(Animal):
def say(self):
print("汪汪")
def say_something(animal):
animal.say()
cat = Cat()
dog = Dog()
say_something(cat)
say_something(dog)
执行结果:
喵喵
汪汪
pass:关键字 "pass"**表示一个空的占位符语句,什么都不做。让代码整体完整。填坑用的。
8. Python的动态性
Python是动态语言,动态语言的典型特征就是:类、对象的属性、方法都可以动态增加和修改。
给类动态添加属性和方法,语法:类名.属性=属性 类名.方法名=函数名
注意: 动态添加的方法,要加上self属性
示例:
class Dog:
name = None
def say(self):
print(f"{self.name}:汪汪")
def action_func(self):
print(f"{self.name}:还能跑,年龄:{self.age}")
dog = Dog()
dog.name = "jack"
dog.say()
Dog.age = 11 # 给类动态添加age属性
Dog.action = action_func # 给类动态添加方法action
dog.action()
执行结果:
jack:汪汪
jack:还能跑,年龄:11
给对象动态添加属性和方法(其他对象作用不到) 给对象动态添加属性和方法
语法: 对象.属性=属性 对象.方法名=函数名
示例:
class Dog:
name = None
def say(self):
print(f"{self.name}:汪汪")
def action_func():
print("还能跑")
dog = Dog()
dog.name = "jack"
dog.say()
dog.age = 11 # 给对象动态添加age属性
dog.action = action_func # 给对象动态添加方法action
dog.action()
dog2 = Dog()
dog2.name = "marry"
# print(dog2.age) # 报错 dog2没有age属性
# dog2.action() # 报错 dog2没有action方法
执行结果:
jack:汪汪
还能跑
9. 枚举类
在某些情况下,一个类的对象是有限且固定的,比如季节类,它只有4个对象;再比如星期,只有星期一到星期日。这种实例有限且固定的类,在Python中被称为枚举类 。枚举类不能被修改,自然也不能被继承。
定义枚举类方式有两种:
- 直接使用Enum列出多个枚举值来创建枚举类
- 通过继承Enum基类来派生枚举类
下面实例,通过Enum列出多个枚举值
import enum
# 定义一个Season枚举类 Enum第一个参数是类名,第二个参数是元组,用于列出所有枚举值
# 每个成员都有name和value两个属性,name是枚举值变量名,value是枚举值的序号,从1开始
Season = enum.Enum('Season', ('SPRING', 'SUMMER', 'FALL', 'WINTER'))
print(Season.SPRING)
print(Season.SPRING.name)
print(Season.SPRING.value)
# 根据枚举变量名来访问枚举对象
print(Season['WINTER'])
# 根据枚举值访问枚举独享
print(Season(4))
# 提供__members__属性,该属性返回一个dict字典,包含了所有枚举实例;通过__members__属性,访问所有枚举值
for name, member in Season.__members__.items():
print(name, '=>', member, ",", member.value)
执行结果:
Season.SPRING
SPRING
1
Season.WINTER
Season.WINTER
SPRING => Season.SPRING , 1
SUMMER => Season.SUMMER , 2
FALL => Season.FALL , 3
WINTER => Season.WINTER , 4
如果要定义更复杂的枚举,则可以通过继承Enum来派生枚举类 ,这种方法的程序可以定义额外方法:
import enum
class Week(enum.Enum):
MON = '星期一'
TUE = '星期二'
WED = '星期三'
THU = '星期四'
FRI = '星期五'
SAT = '星期六'
SUN = '星期日'
def info(self):
print(f"这个是代表星期的一个数值:{self.value}")
print(Week.MON)
print(Week.MON.name)
print(Week.MON.value)
# 通过枚举变量名来访问
print(Week['SAT'])
print(Week['SAT'].name)
print(Week['SAT'].value)
# 通过枚举值来访问
print(Week('星期日'))
print(Week('星期日').name)
print(Week('星期日').value)
# 调用枚举方法info
Week.FRI.info()
# 通过__members__访问所有枚举值
for name, member in Week.__members__.items():
print(name, '=>', member, ',' , member.value)
执行结果:
Week.MON
MON
星期一
Week.SAT
SAT
星期六
Week.SUN
SUN
星期日
这个是代表星期的一个数值:星期五
MON => Week.MON , 星期一
TUE => Week.TUE , 星期二
WED => Week.WED , 星期三
THU => Week.THU , 星期四
FRI => Week.FRI , 星期五
SAT => Week.SAT , 星期六
SUN => Week.SUN , 星期日