七、类和对象

image-20240722161329275

在现实生活中,人是抽象的类,简称人类,像张三,李四,王五,这样真真正正存在的人的实体,我们将这些实体称为对象。

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}")

执行结果:

image-20240722161834576

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("未成年")

执行结果:

image-20240722173210988

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("未成年")

执行结果:

image-20240722174211605

说明 构造对象的时候,调用了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("未成年")

执行结果:

image-20240722174818381

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))

执行结果:

image-20240723101445171

重写str方法:

    def __str__(self):
        return f"姓名:{self.name},年龄:{self.age}"

image-20240723101727873

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()

执行结果:

image-20240723110418928

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()

执行结果:

image-20240723110912355

多继承实例:

# 定义一个水果类,作为父类
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)

执行结果:

image-20240723111446773

重写父类属性和方法

当子类根据业务需求,对父类方法不满意,我们可以在子类里重写父类方法,当然属性也可以重写。

# 定义一个水果类,作为父类
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)

执行结果:

image-20240723111930815

我们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 , 星期日
最近一次更新 3/20/2025, 7:54:12 AM