目 录CONTENT

文章目录

Python(三十二) 类的属性、方法和 @property 详解

Python(三十二) 类的属性、方法和 @property 详解

1. 本篇学习目标

在 Python 面向对象编程中,类不仅可以创建对象,还可以把数据和行为组织在一起。

本篇重点讲清楚这些内容:

  1. 什么是属性
  2. 什么是实例属性
  3. 什么是类属性
  4. 什么是方法
  5. 什么是实例方法
  6. 什么是类方法
  7. 什么是静态方法
  8. selfcls 的区别
  9. 什么是 @property
  10. @property 的 getter、setter、deleter
  11. 属性和方法的命名建议
  12. 常见错误和注意事项

本篇适合在学生已经学过“类和对象”的基础上讲解。

2. 先回顾类和对象

类是模板,对象是根据模板创建出来的具体实例。

例如:

class Student:
    pass


stu = Student()

这里:

Student 是类。
stu 是对象,也叫 Student 类的实例。

类中通常会包含两类内容:

属性:对象或类保存的数据。
方法:对象或类可以执行的行为。

例如学生:

属性:姓名、年龄、成绩、学校
方法:自我介绍、修改成绩、判断是否及格

3. 什么是属性

属性就是类或对象身上保存的数据。

通俗地说:

属性表示“有什么”。

例如,一个学生对象有什么?

姓名
年龄
成绩
班级
学校

代码示例:

class Student:
    pass


stu = Student()

stu.name = "张三"
stu.age = 18
stu.score = 90

print(stu.name)
print(stu.age)
print(stu.score)

输出:

张三
18
90

这里的 nameagescore 都是对象属性。

4. 属性的两大类

Python 类中的属性常见分为两类:

类型 属于谁 是否共享
实例属性 属于具体对象 每个对象各有一份
类属性 属于类本身 所有对象共享

简单理解:

每个对象不同的数据,用实例属性。
所有对象相同的数据,用类属性。

例如:

学生姓名:每个学生不同,用实例属性。
学生成绩:每个学生不同,用实例属性。
学校名称:所有学生相同,可以用类属性。

5. 实例属性

实例属性是属于某个具体对象的数据。

对象也叫实例,所以实例属性也叫对象属性。

最常见的写法是在 __init__() 中定义实例属性。

class Student:
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score


stu = Student("张三", 18, 90)

print(stu.name)
print(stu.age)
print(stu.score)

输出:

张三
18
90

这里:

self.name 是实例属性。
self.age 是实例属性。
self.score 是实例属性。

6. 为什么实例属性写在 init

__init__() 是对象创建后自动执行的初始化方法。

把实例属性写在 __init__() 中有几个好处:

对象创建时就拥有完整数据。
类的结构更清楚。
不容易忘记给对象添加属性。

推荐写法:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

不太推荐初学阶段大量这样写:

class Student:
    pass


stu = Student()
stu.name = "张三"
stu.score = 90

虽然可以运行,但类的结构不清楚。

教学建议:

实例属性优先写在 __init__ 中。

7. 多个对象的实例属性互不影响

同一个类可以创建多个对象。

每个对象都有自己的实例属性。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score


stu1 = Student("张三", 90)
stu2 = Student("李四", 85)

print(stu1.name, stu1.score)
print(stu2.name, stu2.score)

输出:

张三 90
李四 85

修改 stu1 的属性,不会影响 stu2

stu1.score = 95

print(stu1.score)
print(stu2.score)

输出:

95
85

说明:

stu1 和 stu2 是两个不同对象。
它们的实例属性互不影响。

8. 类属性

类属性是属于类本身的数据。

类属性通常写在类里面、方法外面。

示例:

class Student:
    school = "希望中学"

    def __init__(self, name):
        self.name = name


stu1 = Student("张三")
stu2 = Student("李四")

print(Student.school)
print(stu1.school)
print(stu2.school)

输出:

希望中学
希望中学
希望中学

这里:

school 是类属性。
它属于 Student 类。
所有 Student 对象都可以访问它。

9. 类属性适合保存什么

类属性适合保存所有对象共享的数据。

例如:

class Student:
    school = "希望中学"

因为同一批学生可能都属于同一所学校。

再比如:

class Product:
    tax_rate = 0.13

如果所有商品使用同一个税率,可以把税率写成类属性。

适合类属性的数据:

所有对象共享的学校名称
统一折扣率
统一税率
对象计数器
配置常量

不适合类属性的数据:

每个学生的姓名
每个学生的成绩
每个商品的库存
每个账户的余额

这些应该使用实例属性。

10. 实例属性和类属性对比

对比项 实例属性 类属性
属于谁 具体对象 类本身
定义位置 通常在 __init__ 类中、方法外
是否共享 每个对象各有一份 所有对象共享
访问方式 对象.属性 类名.属性
常见用途 姓名、成绩、余额 学校、税率、计数器

示例:

class Student:
    school = "希望中学"

    def __init__(self, name, score):
        self.name = name
        self.score = score


stu = Student("张三", 90)

print(stu.name)
print(stu.score)
print(Student.school)

输出:

张三
90
希望中学

11. 修改类属性

修改类属性时,推荐通过类名修改。

class Student:
    school = "希望中学"


stu1 = Student()
stu2 = Student()

Student.school = "实验中学"

print(stu1.school)
print(stu2.school)
print(Student.school)

输出:

实验中学
实验中学
实验中学

因为 school 是类属性,通过类名修改后,所有对象访问到的都是新值。

12. 通过对象修改类属性的陷阱

看下面的代码:

class Student:
    school = "希望中学"


stu1 = Student()
stu2 = Student()

stu1.school = "实验中学"

print(stu1.school)
print(stu2.school)
print(Student.school)

输出:

实验中学
希望中学
希望中学

为什么?

因为:

stu1.school = "实验中学"
不是修改类属性。
而是给 stu1 新增了一个同名实例属性 school。

此时:

stu1 有自己的 school。
stu2 没有自己的 school,所以去类中找 school。
Student.school 仍然是原来的值。

教学记忆:

类属性用类名改,不要随手用对象改。

13. 属性查找顺序

当我们访问:

对象.属性

Python 通常会按下面的顺序查找:

1. 先找对象自己的实例属性。
2. 如果对象没有,再去类中找类属性。

示例:

class Student:
    school = "希望中学"


stu = Student()

print(stu.school)

stu.school = "实验中学"

print(stu.school)
print(Student.school)

输出:

希望中学
实验中学
希望中学

第一次:

stu 没有实例属性 school,所以找到类属性 Student.school。

第二次:

stu 自己有实例属性 school,所以优先使用实例属性。

14. 类属性统计对象数量

类属性常见用途之一是统计创建了多少个对象。

class Student:
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1


stu1 = Student("张三")
stu2 = Student("李四")
stu3 = Student("王五")

print(Student.count)

输出:

3

这里:

count 是类属性。
每创建一个 Student 对象,count 就加 1。

注意:

Student.count += 1

这里推荐写类名 Student,让学生清楚这是在修改类属性。

15. 可变类属性的坑

类属性如果是列表、字典、集合这类可变对象,要非常小心。

看例子:

class Student:
    hobbies = []

    def __init__(self, name):
        self.name = name


stu1 = Student("张三")
stu2 = Student("李四")

stu1.hobbies.append("篮球")

print(stu1.hobbies)
print(stu2.hobbies)

输出:

['篮球']
['篮球']

为什么 stu2 也有篮球?

因为:

hobbies 是类属性。
所有对象共享同一个列表。

如果每个学生应该有自己的爱好列表,就应该写成实例属性。

class Student:
    def __init__(self, name):
        self.name = name
        self.hobbies = []


stu1 = Student("张三")
stu2 = Student("李四")

stu1.hobbies.append("篮球")

print(stu1.hobbies)
print(stu2.hobbies)

输出:

['篮球']
[]

教学记忆:

每个对象都应该独立拥有的数据,不要写成可变类属性。

16. 什么是方法

方法就是定义在类里面的函数。

通俗地说:

属性表示对象有什么。
方法表示对象能做什么。

例如学生:

属性:姓名、年龄、成绩
方法:自我介绍、学习、修改成绩

示例:

class Student:
    def introduce(self):
        print("大家好,我是学生")


stu = Student()
stu.introduce()

输出:

大家好,我是学生

17. 方法的三大类

Python 类中的方法常见分为三类:

方法类型 装饰器 第一个参数 主要操作对象
实例方法 self 实例对象
类方法 @classmethod cls 类本身
静态方法 @staticmethod 无固定参数 普通工具逻辑

简单理解:

实例方法操作对象自己的数据。
类方法操作类自己的数据。
静态方法只是放在类里的普通函数。

18. 实例方法

实例方法是最常见的方法。

特点:

定义在类中。
第一个参数是 self。
通过对象调用。
可以访问实例属性。

示例:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def show_info(self):
        print(f"{self.name} 的成绩是 {self.score}")


stu = Student("张三", 90)
stu.show_info()

输出:

张三 的成绩是 90

这里:

show_info() 是实例方法。
self 表示当前调用方法的对象。

19. self 是什么

self 表示当前对象。

通俗记忆:

谁调用方法,self 就是谁。

示例:

class Student:
    def __init__(self, name):
        self.name = name

    def say_name(self):
        print(self.name)


stu1 = Student("张三")
stu2 = Student("李四")

stu1.say_name()
stu2.say_name()

输出:

张三
李四

执行:

stu1.say_name()

此时 selfstu1

执行:

stu2.say_name()

此时 selfstu2

20. 实例方法中访问属性

实例方法中访问实例属性,要写:

self.属性名

示例:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def is_passed(self):
        return self.score >= 60


stu = Student("张三", 90)

print(stu.is_passed())

输出:

True

如果不写 self

def is_passed(self):
    return score >= 60

会报错,因为 Python 不知道 score 是谁。

21. 实例方法可以修改实例属性

示例:修改学生成绩。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def update_score(self, new_score):
        self.score = new_score


stu = Student("张三", 80)

stu.update_score(95)

print(stu.score)

输出:

95

这里:

self.score = new_score

修改的是当前对象自己的 score 属性。

22. 实例方法可以有参数和返回值

方法和普通函数一样,可以有参数,也可以有返回值。

class Calculator:
    def add(self, a, b):
        return a + b


calc = Calculator()

result = calc.add(3, 5)
print(result)

输出:

8

注意:

self 是 Python 自动传入的。
调用时只需要传 a 和 b。

调用:

calc.add(3, 5)

不需要写:

calc.add(calc, 3, 5)

23. 类方法

类方法使用 @classmethod 装饰器。

类方法第一个参数通常写成 cls

cls 表示当前类。

示例:

class Student:
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1

    @classmethod
    def show_count(cls):
        print(f"当前学生数量:{cls.count}")


stu1 = Student("张三")
stu2 = Student("李四")

Student.show_count()

输出:

当前学生数量:2

这里:

show_count() 是类方法。
cls 表示 Student 类。
cls.count 表示访问类属性 count。

24. cls 是什么

cls 表示当前类。

它和 self 类似,但含义不同:

self 表示当前对象。
cls 表示当前类。

示例:

class Student:
    school = "希望中学"

    @classmethod
    def show_school(cls):
        print(cls.school)


Student.show_school()

输出:

希望中学

这里的 cls 就是 Student

教学记忆:

self 管对象。
cls 管类。

25. 类方法适合做什么

类方法适合处理和类本身有关的逻辑。

常见用途:

访问或修改类属性
统计对象数量
提供另一种创建对象的方式
根据字符串、字典等数据创建对象

示例:根据字符串创建学生对象。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    @classmethod
    def from_string(cls, text):
        name, score = text.split(",")
        return cls(name, int(score))


stu = Student.from_string("张三,90")

print(stu.name)
print(stu.score)

输出:

张三
90

这里:

return cls(name, int(score))

表示调用当前类来创建对象。

26. 为什么类方法中用 cls 而不是类名

看这个例子:

class Student:
    def __init__(self, name):
        self.name = name

    @classmethod
    def create(cls, name):
        return cls(name)

如果以后有子类继承 Studentcls 可以代表调用这个方法的类。

虽然初学阶段不需要深入继承,但可以先记住:

类方法里推荐用 cls,而不是把类名写死。

不推荐:

return Student(name)

推荐:

return cls(name)

这样代码更灵活。

27. 静态方法

静态方法使用 @staticmethod 装饰器。

静态方法没有固定的第一个参数。

它既不自动接收 self,也不自动接收 cls

示例:

class MathTool:
    @staticmethod
    def add(a, b):
        return a + b


print(MathTool.add(3, 5))

输出:

8

静态方法可以理解为:

放在类里面的普通函数。

28. 静态方法适合做什么

静态方法适合放和类主题相关,但不需要访问对象和类的数据的工具逻辑。

例如:

class ScoreTool:
    @staticmethod
    def is_valid_score(score):
        return 0 <= score <= 100


print(ScoreTool.is_valid_score(90))
print(ScoreTool.is_valid_score(150))

输出:

True
False

这里 is_valid_score() 和“成绩”这个主题有关。

但它不需要:

self
cls
实例属性
类属性

所以可以写成静态方法。

29. 三种方法对比

方法类型 装饰器 第一个参数 访问实例属性 访问类属性 常见用途
实例方法 self 可以 可以 操作对象数据
类方法 @classmethod cls 不直接操作具体对象 可以 操作类数据、创建对象
静态方法 @staticmethod 不自动访问 不自动访问 工具函数

示例:

class Demo:
    class_value = 100

    def __init__(self, value):
        self.value = value

    def instance_method(self):
        print(self.value)

    @classmethod
    def class_method(cls):
        print(cls.class_value)

    @staticmethod
    def static_method(a, b):
        return a + b

教学顺序建议:

先讲实例方法。
再讲类方法。
最后讲静态方法。

30. 方法调用方式

实例方法通常通过对象调用。

stu.show_info()

类方法通常通过类调用。

Student.show_count()

静态方法通常通过类调用。

MathTool.add(3, 5)

完整示例:

class Demo:
    def instance_method(self):
        print("实例方法")

    @classmethod
    def class_method(cls):
        print("类方法")

    @staticmethod
    def static_method():
        print("静态方法")


demo = Demo()

demo.instance_method()
Demo.class_method()
Demo.static_method()

输出:

实例方法
类方法
静态方法

31. 类方法和静态方法也能通过对象调用吗

可以,但教学中建议通过类名调用。

class Demo:
    @classmethod
    def class_method(cls):
        print("类方法")

    @staticmethod
    def static_method():
        print("静态方法")


demo = Demo()

demo.class_method()
demo.static_method()

可以运行。

但是更推荐:

Demo.class_method()
Demo.static_method()

原因:

这样更清楚地表达:这些方法主要和类有关,不是某个具体对象的行为。

32. 什么是 @property

@property 是 Python 提供的一种装饰器。

它可以把一个方法变成像属性一样访问。

先看普通方法写法:

class Student:
    def __init__(self, score):
        self.__score = score

    def get_score(self):
        return self.__score


stu = Student(90)

print(stu.get_score())

输出:

90

使用 @property 后:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score


stu = Student(90)

print(stu.score)

输出:

90

注意:

stu.score

看起来像访问属性,但背后实际调用了 score() 方法。

33. @property 的作用

@property 的作用可以总结为:

让方法像属性一样使用。

它的好处是:

外部使用简单。
内部仍然可以写逻辑。
可以控制属性读取。
可以控制属性修改。
可以做数据校验。
可以创建只读属性。

例如:

print(stu.score)

比:

print(stu.get_score())

更像普通属性访问。

但内部仍然可以保护真实数据。

34. 为什么需要 @property

先看直接公开属性:

class Student:
    def __init__(self, score):
        self.score = score


stu = Student(90)
stu.score = -100

print(stu.score)

输出:

-100

成绩变成负数,不合理。

如果使用普通 getter/setter:

class Student:
    def __init__(self, score):
        self.__score = 0
        self.set_score(score)

    def get_score(self):
        return self.__score

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            print("成绩不合法")

调用:

stu = Student(90)
stu.set_score(95)
print(stu.get_score())

可以控制数据,但用起来不像普通属性。

@property 可以兼顾两者:

外部像访问属性一样简单。
内部像方法一样可以控制逻辑。

35. @property 基本写法

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

使用:

stu = Student(90)
print(stu.score)

注意:

定义时像方法。
使用时像属性。

定义时:

def score(self):
    return self.__score

使用时:

stu.score

不要写:

stu.score()

因为加了 @property 后,它就应该像属性一样使用。

36. @property 创建只读属性

如果只定义 @property,不定义 setter,那么这个属性就是只读的。

示例:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14 * self.radius * self.radius


circle = Circle(5)

print(circle.area)

输出:

78.5

如果尝试修改:

circle.area = 100

会报错。

为什么 area 适合只读?

因为:

面积是根据半径计算出来的。
不应该直接手动修改面积。

37. 计算属性

通过 @property 定义的属性,可以是计算出来的。

例如矩形面积:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height


rect = Rectangle(3, 4)
print(rect.area)

rect.width = 10
print(rect.area)

输出:

12
40

这里:

area 没有直接保存。
每次访问 rect.area 时都会重新计算。

好处:

width 或 height 改变后,area 自动得到新结果。

38. @property 的 setter

如果希望属性可以被修改,并且修改时进行校验,可以定义 setter。

写法:

@属性名.setter
def 属性名(self, value):
    ...

示例:

class Student:
    def __init__(self, score):
        self.__score = 0
        self.score = score

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        if 0 <= value <= 100:
            self.__score = value
        else:
            print("成绩必须在 0 到 100 之间")


stu = Student(90)

print(stu.score)

stu.score = 95
print(stu.score)

stu.score = 150
print(stu.score)

输出:

90
95
成绩必须在 0 到 100 之间
95

这里:

stu.score = 95

看起来是赋值,其实会自动调用 setter。

39. setter 的执行过程

看这段代码:

stu.score = 95

Python 会自动调用:

@score.setter
def score(self, value):
    ...

其中:

self 是 stu。
value 是 95。

所以 setter 中可以判断:

if 0 <= value <= 100:
    self.__score = value

教学记忆:

读取属性走 @property。
修改属性走 @属性.setter。

40. @property 的 deleter

如果希望支持删除属性,可以定义 deleter。

写法:

@属性名.deleter
def 属性名(self):
    ...

示例:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

    @score.deleter
    def score(self):
        print("删除成绩")
        del self.__score


stu = Student(90)

print(stu.score)

del stu.score

输出:

90
删除成绩

执行:

del stu.score

会自动调用 deleter。

实际教学中,deleter 用得不如 getter 和 setter 多。

41. @property 完整结构

完整结构如下:

class 类名:
    def __init__(self, value):
        self.__value = value

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, new_value):
        self.__value = new_value

    @value.deleter
    def value(self):
        del self.__value

使用:

obj.value          # 调用 getter
obj.value = 100    # 调用 setter
del obj.value      # 调用 deleter

教学时重点掌握:

@property
@属性名.setter

deleter 可以作为了解内容。

42. @property 和私有属性配合

@property 经常和私有属性一起使用。

常见写法:

class Student:
    def __init__(self, score):
        self.__score = 0
        self.score = score

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        if 0 <= value <= 100:
            self.__score = value
        else:
            print("成绩不合法")

外部访问:

stu.score

内部保存:

self.__score

好处:

外部不知道内部真实属性名。
外部不能直接乱改私有属性。
修改时可以统一校验。

43. 避免 property 无限递归

这是 @property 中非常常见的错误。

错误写法:

class Student:
    @property
    def score(self):
        return self.score

为什么错?

因为:

访问 self.score 会再次调用 score 这个 property。
然后又访问 self.score。
一直重复,造成无限递归。

正确写法:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

教学记忆:

property 外面叫 score。
内部真实数据用 __score 或 _score 保存。

44. property 名称和真实属性名称

推荐命名:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

这里:

score 是给外部使用的属性名。
__score 是内部真实保存数据的属性名。

也有人使用单下划线:

class Student:
    def __init__(self, score):
        self._score = score

    @property
    def score(self):
        return self._score

两种都常见。

教学中可以先使用双下划线,强调私有属性。

45. @property 与普通 getter/setter 对比

普通 getter/setter:

class Student:
    def __init__(self, score):
        self.__score = score

    def get_score(self):
        return self.__score

    def set_score(self, score):
        self.__score = score

调用:

stu.get_score()
stu.set_score(90)

@property 写法:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        self.__score = value

调用:

stu.score
stu.score = 90

对比:

写法 读取 修改 特点
getter/setter get_score() set_score(90) 明确是方法
@property score score = 90 像属性一样自然

46. 什么时候使用 @property

适合使用 @property 的场景:

需要把方法伪装成属性
需要创建只读属性
需要在赋值时做数据校验
需要隐藏内部真实属性
需要计算属性
希望外部访问方式更简洁

示例:

成绩 score 需要校验 0 到 100
年龄 age 需要校验 0 到 150
面积 area 根据宽高计算
余额 balance 不希望外部随便改

不一定需要 @property 的场景:

只是普通公开数据,没有校验逻辑。

例如:

class Book:
    def __init__(self, title):
        self.title = title

如果 title 没有特殊限制,直接公开也可以。

47. @property 案例:年龄校验

class Person:
    def __init__(self, name, age):
        self.name = name
        self.__age = 0
        self.age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, value):
        if 0 <= value <= 150:
            self.__age = value
        else:
            print("年龄必须在 0 到 150 之间")


person = Person("张三", 18)

print(person.age)

person.age = -5
print(person.age)

person.age = 20
print(person.age)

输出:

18
年龄必须在 0 到 150 之间
18
20

这里:

age 看起来是普通属性。
但赋值时会自动校验。

48. @property 案例:商品价格

class Product:
    def __init__(self, name, price):
        self.name = name
        self.__price = 0
        self.price = price

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value >= 0:
            self.__price = value
        else:
            print("价格不能小于 0")


product = Product("苹果", 5)

print(product.price)

product.price = -10
print(product.price)

输出:

5
价格不能小于 0
5

49. @property 案例:只读面积

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height

    @property
    def perimeter(self):
        return (self.width + self.height) * 2


rect = Rectangle(3, 4)

print(rect.area)
print(rect.perimeter)

rect.width = 10

print(rect.area)
print(rect.perimeter)

输出:

12
14
40
28

这里:

area 和 perimeter 是计算属性。
它们没有 setter,所以是只读属性。

50. @property 案例:温度转换

class Temperature:
    def __init__(self, celsius):
        self.__celsius = 0
        self.celsius = celsius

    @property
    def celsius(self):
        return self.__celsius

    @celsius.setter
    def celsius(self, value):
        if value >= -273.15:
            self.__celsius = value
        else:
            print("温度不能低于绝对零度")

    @property
    def fahrenheit(self):
        return self.__celsius * 9 / 5 + 32


temp = Temperature(25)

print(temp.celsius)
print(temp.fahrenheit)

temp.celsius = 100
print(temp.fahrenheit)

输出:

25
77.0
212.0

这里:

celsius 可以读取和修改。
fahrenheit 根据 celsius 计算,是只读属性。

51. 实例方法、类方法、静态方法与 property 综合案例

class Student:
    school = "希望中学"
    count = 0

    def __init__(self, name, score):
        self.name = name
        self.__score = 0
        self.score = score
        Student.count += 1

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        if Student.is_valid_score(value):
            self.__score = value
        else:
            print("成绩必须在 0 到 100 之间")

    def show_info(self):
        print(f"{self.name},学校:{Student.school},成绩:{self.score}")

    def is_passed(self):
        return self.score >= 60

    @classmethod
    def show_count(cls):
        print(f"学生数量:{cls.count}")

    @staticmethod
    def is_valid_score(score):
        return 0 <= score <= 100


stu1 = Student("张三", 90)
stu2 = Student("李四", 55)

stu1.show_info()
stu2.show_info()

print(stu1.is_passed())
print(stu2.is_passed())

stu2.score = 75
stu2.show_info()

Student.show_count()

输出:

张三,学校:希望中学,成绩:90
李四,学校:希望中学,成绩:55
True
False
李四,学校:希望中学,成绩:75
学生数量:2

这个案例中:

school 和 count 是类属性。
name 和 __score 是实例属性。
show_info() 和 is_passed() 是实例方法。
show_count() 是类方法。
is_valid_score() 是静态方法。
score 是 property 属性。

52. 常见错误 1:实例方法忘记写 self

错误写法:

class Student:
    def show_info():
        print("学生信息")


stu = Student()
stu.show_info()

会报错。

正确写法:

class Student:
    def show_info(self):
        print("学生信息")

记忆:

实例方法第一个参数写 self。

53. 常见错误 2:在实例方法中访问属性忘记 self

错误写法:

class Student:
    def __init__(self, name):
        self.name = name

    def show_name(self):
        print(name)

正确写法:

class Student:
    def __init__(self, name):
        self.name = name

    def show_name(self):
        print(self.name)

记忆:

访问实例属性要写 self.属性名。

54. 常见错误 3:把实例属性写成类属性

错误写法:

class Student:
    name = ""
    score = 0

如果 namescore 是每个学生不同的数据,就不应该写成类属性。

推荐:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

记忆:

每个对象不同的数据,写成实例属性。

55. 常见错误 4:可变类属性被共享

错误写法:

class Student:
    hobbies = []

多个对象会共享同一个列表。

如果每个对象应该有自己的列表,应该写:

class Student:
    def __init__(self):
        self.hobbies = []

56. 常见错误 5:通过对象修改类属性

不推荐:

stu.school = "实验中学"

如果 school 是类属性,推荐:

Student.school = "实验中学"

通过对象赋值可能创建同名实例属性,导致学生误解。

57. 常见错误 6:类方法忘记 @classmethod

错误写法:

class Student:
    count = 0

    def show_count(cls):
        print(cls.count)


Student.show_count()

可能会报错,因为 Python 不会自动把类传给 cls

正确写法:

class Student:
    count = 0

    @classmethod
    def show_count(cls):
        print(cls.count)

58. 常见错误 7:静态方法里使用 self

错误写法:

class MathTool:
    @staticmethod
    def add(a, b):
        print(self)
        return a + b

静态方法不会自动接收 self

如果需要访问实例属性,就不应该写成静态方法,而应该写成实例方法。

59. 常见错误 8:property 使用时加括号

错误写法:

print(stu.score())

如果 score 使用了 @property,应该像属性一样使用:

print(stu.score)

记忆:

property 定义时像方法,使用时像属性。

60. 常见错误 9:property 中返回自己导致递归

错误写法:

class Student:
    @property
    def score(self):
        return self.score

正确写法:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

61. 常见错误 10:setter 名字不一致

错误写法:

class Student:
    @property
    def score(self):
        return self.__score

    @score.setter
    def set_score(self, value):
        self.__score = value

虽然语法可能不一定立刻按预期工作,但不推荐这样写。

推荐 getter 和 setter 使用同一个名字:

class Student:
    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        self.__score = value

记忆:

property 名和 setter 方法名保持一致。

62. 常见错误 11:只定义 getter 却尝试赋值

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14 * self.radius * self.radius


circle = Circle(5)
circle.area = 100

会报错。

原因:

area 只有 getter,没有 setter,是只读属性。

63. 常见错误 12:过度使用 property

不是所有属性都要写成 @property

如果属性没有校验逻辑,也不是计算属性,直接公开通常更简单。

例如:

class Book:
    def __init__(self, title):
        self.title = title

不一定非要写成:

@property
def title(self):
    return self.__title

教学总结:

需要控制访问时用 property。
简单普通属性可以直接公开。

64. 注意事项 1:属性和方法命名要清楚

属性名通常使用名词:

name
age
score
price
stock
balance

方法名通常使用动词或动词短语:

show_info()
update_score()
calculate_area()
is_passed()
add_stock()

好的命名可以让学生一眼看出:

这是数据,还是行为。

65. 注意事项 2:不要让属性和方法同名

不推荐:

class Student:
    def __init__(self):
        self.score = 90

    def score(self):
        return 100

属性和方法同名会造成混乱。

推荐:

self.score = 90

def get_score(self):
    ...

或者:

@property
def score(self):
    ...

但不要同时用普通属性和普通方法占用同一个名字。

66. 注意事项 3:实例方法不要承担太多职责

一个方法最好只做一件主要事情。

推荐:

def update_score(self, score):
    self.score = score

def is_passed(self):
    return self.score >= 60

不推荐一个方法里同时:

修改成绩
打印信息
保存文件
发送通知
计算排名

教学记忆:

一个方法,一个主要任务。

67. 注意事项 4:类属性不要存放对象独有状态

对象独有状态应该用实例属性。

例如:

学生成绩
账户余额
商品库存
用户密码

这些都应该属于每个对象自己。

不应该写成类属性。

68. 注意事项 5:类方法适合处理类级别逻辑

类方法不是实例方法的替代品。

如果逻辑需要访问某个具体对象的数据,就应该使用实例方法。

适合类方法:

显示对象总数
修改全局类配置
从字符串创建对象
从字典创建对象

不适合类方法:

修改某个学生的成绩
显示某个学生的姓名
修改某个账户的余额

这些应该是实例方法。

69. 注意事项 6:静态方法不要强行使用

静态方法只是放在类里的普通函数。

如果一个函数和类没有明显关系,直接写成普通函数也可以。

适合静态方法:

成绩合法性检查
价格格式化
和类主题密切相关的小工具

不适合静态方法:

完全和类无关的工具函数

70. 注意事项 7:property 适合表现为“属性”的内容

适合 @property

score
age
area
balance
price

不适合 @property

save_to_file
send_email
update_database
download_data

这些明显是动作,应该写成普通方法。

教学判断:

像数据,用 property。
像动作,用方法。

71. 注意事项 8:setter 中要考虑边界值

例如成绩:

0 <= score <= 100

要明确:

0 是否合法?
100 是否合法?
-1 是否非法?
101 是否非法?

年龄:

0 <= age <= 150

价格:

price >= 0

库存:

stock >= 0

边界值是课堂练习的好材料。

72. 综合案例:银行账户

class BankAccount:
    bank_name = "Python 银行"
    count = 0

    def __init__(self, owner, balance=0):
        self.owner = owner
        self.__balance = 0
        self.balance = balance
        BankAccount.count += 1

    @property
    def balance(self):
        return self.__balance

    @balance.setter
    def balance(self, value):
        if value >= 0:
            self.__balance = value
        else:
            print("余额不能小于 0")

    def deposit(self, money):
        if money > 0:
            self.__balance += money
        else:
            print("存款金额必须大于 0")

    def withdraw(self, money):
        if money <= 0:
            print("取款金额必须大于 0")
        elif money > self.__balance:
            print("余额不足")
        else:
            self.__balance -= money

    def show_info(self):
        print(f"{self.owner},余额:{self.balance}")

    @classmethod
    def show_count(cls):
        print(f"账户数量:{cls.count}")

    @staticmethod
    def is_valid_money(money):
        return money > 0


account = BankAccount("张三", 1000)

account.deposit(500)
account.withdraw(300)
account.show_info()

account.balance = -100
account.show_info()

BankAccount.show_count()
print(BankAccount.is_valid_money(50))

输出:

张三,余额:1200
余额不能小于 0
张三,余额:1200
账户数量:1
True

这个案例中:

bank_name、count 是类属性。
owner、__balance 是实例属性。
balance 是 property。
deposit、withdraw、show_info 是实例方法。
show_count 是类方法。
is_valid_money 是静态方法。

73. 综合案例:商品类

class Product:
    tax_rate = 0.13

    def __init__(self, name, price, stock):
        self.name = name
        self.__price = 0
        self.__stock = 0
        self.price = price
        self.stock = stock

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value >= 0:
            self.__price = value
        else:
            print("价格不能小于 0")

    @property
    def stock(self):
        return self.__stock

    @stock.setter
    def stock(self, value):
        if value >= 0:
            self.__stock = value
        else:
            print("库存不能小于 0")

    @property
    def total_value(self):
        return self.price * self.stock

    def sell(self, count):
        if count <= 0:
            print("销售数量必须大于 0")
        elif count > self.stock:
            print("库存不足")
        else:
            self.stock -= count

    def show_info(self):
        print(f"{self.name},价格:{self.price},库存:{self.stock}")

    @classmethod
    def set_tax_rate(cls, rate):
        if rate >= 0:
            cls.tax_rate = rate
        else:
            print("税率不能小于 0")

    @staticmethod
    def is_valid_count(count):
        return count > 0


product = Product("苹果", 5, 100)

product.show_info()
product.sell(3)
product.show_info()

print(product.total_value)

Product.set_tax_rate(0.1)
print(Product.tax_rate)

输出:

苹果,价格:5,库存:100
苹果,价格:5,库存:97
485
0.1

74. 课堂练习 1:实例属性和实例方法

要求:

  1. 定义 Student 类。
  2. __init__ 中定义 namescore
  3. 定义实例方法 show_info()
  4. 创建对象并调用方法。

参考答案:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def show_info(self):
        print(f"{self.name}: {self.score}")


stu = Student("张三", 90)
stu.show_info()

75. 课堂练习 2:类属性

要求:

  1. 定义 Student 类。
  2. 添加类属性 school
  3. 创建两个学生对象。
  4. 输出它们的学校名称。

参考答案:

class Student:
    school = "希望中学"

    def __init__(self, name):
        self.name = name


stu1 = Student("张三")
stu2 = Student("李四")

print(stu1.school)
print(stu2.school)
print(Student.school)

76. 课堂练习 3:统计对象数量

要求:

  1. 定义类属性 count
  2. 每创建一个对象,count 加 1。
  3. 输出创建的对象数量。

参考答案:

class Student:
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1


stu1 = Student("张三")
stu2 = Student("李四")

print(Student.count)

77. 课堂练习 4:类方法

要求:

  1. 定义 Student 类。
  2. 类属性 count 记录学生数量。
  3. 定义类方法 show_count() 显示学生数量。

参考答案:

class Student:
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1

    @classmethod
    def show_count(cls):
        print(f"学生数量:{cls.count}")


stu1 = Student("张三")
stu2 = Student("李四")

Student.show_count()

78. 课堂练习 5:静态方法

要求:

  1. 定义 ScoreTool 类。
  2. 定义静态方法 is_valid_score(score)
  3. 判断成绩是否在 0100 之间。

参考答案:

class ScoreTool:
    @staticmethod
    def is_valid_score(score):
        return 0 <= score <= 100


print(ScoreTool.is_valid_score(90))
print(ScoreTool.is_valid_score(150))

79. 课堂练习 6:只读 property

要求:

  1. 定义 Circle 类。
  2. radius 属性。
  3. 使用 @property 定义只读属性 area

参考答案:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14 * self.radius * self.radius


circle = Circle(5)
print(circle.area)

80. 课堂练习 7:带 setter 的 property

要求:

  1. 定义 Person 类。
  2. 使用私有属性保存年龄。
  3. 使用 @property 读取年龄。
  4. 使用 setter 修改年龄,并校验年龄在 0150 之间。

参考答案:

class Person:
    def __init__(self, age):
        self.__age = 0
        self.age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, value):
        if 0 <= value <= 150:
            self.__age = value
        else:
            print("年龄不合法")


person = Person(18)
print(person.age)

person.age = -5
print(person.age)

81. 课堂练习 8:判断输出

阅读代码,判断输出:

class Student:
    school = "希望中学"

    def __init__(self, name):
        self.name = name


stu1 = Student("张三")
stu2 = Student("李四")

stu1.school = "实验中学"

print(stu1.school)
print(stu2.school)
print(Student.school)

答案:

实验中学
希望中学
希望中学

解释:

stu1.school = "实验中学" 创建了 stu1 自己的实例属性。
没有修改 Student.school。

82. 课堂练习 9:找错误

下面代码有什么问题?

class Student:
    @property
    def score(self):
        return self.score

答案:

return self.score 会再次调用 score 这个 property,导致无限递归。

正确写法:

class Student:
    def __init__(self, score):
        self.__score = score

    @property
    def score(self):
        return self.__score

83. 课堂练习 10:综合练习

要求:

定义一个 Product 类:

  1. 类属性 tax_rate = 0.13
  2. 实例属性 name
  3. 使用 @property 封装 price,价格不能小于 0
  4. 实例方法 show_info() 显示商品信息。
  5. 类方法 set_tax_rate() 修改税率。
  6. 静态方法 is_valid_price() 判断价格是否合法。

参考答案:

class Product:
    tax_rate = 0.13

    def __init__(self, name, price):
        self.name = name
        self.__price = 0
        self.price = price

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if Product.is_valid_price(value):
            self.__price = value
        else:
            print("价格不能小于 0")

    def show_info(self):
        print(f"{self.name},价格:{self.price},税率:{Product.tax_rate}")

    @classmethod
    def set_tax_rate(cls, rate):
        if rate >= 0:
            cls.tax_rate = rate
        else:
            print("税率不能小于 0")

    @staticmethod
    def is_valid_price(price):
        return price >= 0


product = Product("苹果", 5)
product.show_info()

product.price = -10
product.show_info()

Product.set_tax_rate(0.1)
product.show_info()

84. 教学总结

Python 类中的属性、方法和 @property 是面向对象编程的重要基础。

一句话总结:

属性保存数据,方法表示行为,property 让方法像属性一样使用。

核心知识:

  1. 实例属性属于具体对象。
  2. 类属性属于类,被所有对象共享。
  3. 每个对象不同的数据,用实例属性。
  4. 所有对象共享的数据,用类属性。
  5. 实例方法第一个参数是 self
  6. self 表示当前对象。
  7. 类方法使用 @classmethod
  8. 类方法第一个参数是 cls
  9. cls 表示当前类。
  10. 静态方法使用 @staticmethod
  11. 静态方法不自动接收 selfcls
  12. @property 可以让方法像属性一样访问。
  13. @属性.setter 可以控制属性赋值。
  14. 只定义 getter 的 property 是只读属性。
  15. property 内部不要返回自己,避免无限递归。

常用模板:

class 类名:
    类属性 = 值

    def __init__(self, 参数):
        self.实例属性 = 参数

    def 实例方法(self):
        pass

    @classmethod
    def 类方法(cls):
        pass

    @staticmethod
    def 静态方法():
        pass

property 模板:

class 类名:
    def __init__(self, value):
        self.__value = 0
        self.value = value

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, new_value):
        if 条件:
            self.__value = new_value
        else:
            print("数据不合法")

课堂记忆口诀:

对象自己的,实例属性。
大家共享的,类属性。
self 管对象,
cls 管类。
实例方法最常用,
类方法管类数据,
静态方法像工具。
property 像属性,
setter 做校验。

最后记住:

属性、方法和 property 的目标,
不是让代码变复杂,
而是让数据更清楚、行为更明确、使用更自然。
0
博主关闭了当前页面的评论