《Python 类设计模式:属性分类(类属性 VS 实例属性)与方法类型(实例 / 类 / 静态)详解》

发布于:2025-07-22 ⋅ 阅读:(14) ⋅ 点赞:(0)

Python 类和对象:从 "图纸" 到 "实物" 的编程思维

        面向对象编程(Object-Oriented Programming,简称OOP )是一种通过组织对象来编程的方法。

1.初识类和对象:用生活例子看透核心概念

 1.1类-class

        物与类聚

1.1.1概念

        类是对一类对象的抽象,是对象的模板或蓝图。它定义了对象的属性(特征)方法(功能)。例如 ‘人’类。

1.1.2创建

  • 数据成员:表明事物的特征。 相当于变量

  • 方法成员:表明事物的功能。 相当于函数

  • 通过class关键字定义类。

  • 类的创建语句语法:

'''
class 类名 (继承列表):
	实例属性(类内的变量) 定义
    实例方法(类内的函数method) 定义
    类变量(class variable) 定义
    类方法(@classmethod) 定义
    静态方法(@staticmethod) 定义
'''
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(f"{self.name} says Woof!")
  • 类名就是标识符,建议首字母大写

  • 类名实质上就是变量,它绑定一个类

  • self代表类实例化的对象本身

1.2对象-object

        人以群分,每个人就是一个对象。

1.2.1概念

        对象是类的 "实例化"—— 用类创建出的具体个体。每个对象都有自己的属性值,但共享类的方法。

1.2.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.属性和方法

        类的属性和方法是类的核心组成部分,它们用于定义类的状态和行为。

2.1属性:对象的 "特征" 分类

        属性是对象的特征(比如名字、年龄、颜色),按归属可分为实例属性类属性

2.1.1实例属性:每个对象独有的特征

        实例属性是每个对象自己的 "个性特征"—— 不同对象可以有不同的值。
        定义方式:在初始化方法__init__中通过self.属性名定义。

class Dog:
    # 初始化方法:创建对象时自动调用,给对象设置初始属性
    def __init__(self, name, age):  # self代表当前对象
        self.name = name  # 实例属性:名字(每个狗不同)
        self.age = age    # 实例属性:年龄(每个狗不同)

# 创建对象时传入属性值
dog1 = Dog("旺财", 3)  # 旺财3岁
dog2 = Dog("小白", 2)  # 小白2岁

# 访问实例属性:对象名.属性名
print(dog1.name)  # 输出:旺财
print(dog2.age)   # 输出:2

        使用场景:描述对象的个性化特征(如人的姓名、手机的价格)。 

2.1.2类属性:所有对象共享的特征

        类属性是类本身的属性,所有对象共享同一个值 —— 改一次,所有对象都受影响。
        定义方式:直接在类中定义(不在__init__里)。

class Dog:
    # 类属性:所有狗共享的特征(比如都属于"犬科")
    species = "犬科"  # 类属性:定义在类中,不在__init__里

    def __init__(self, name):
        self.name = name  # 实例属性

# 创建对象
dog1 = Dog("旺财")
dog2 = Dog("小白")

# 访问类属性:类名.属性名 或 对象名.属性名
print(Dog.species)  # 输出:犬科(推荐用类名访问)
print(dog1.species) # 输出:犬科(对象也能访问)

# 修改类属性:所有对象都会受影响
Dog.species = "哺乳纲犬科"
print(dog2.species) # 输出:哺乳纲犬科

         使用场景:存储所有对象的共性数据(如学生类的 "学校名称"、汽车类的 "燃料类型")。

2.2方法:对象的 "功能" 分类

        方法是对象的功能(比如说话、计算、移动),按作用范围可分为实例方法类方法静态方法

2.2.1实例方法:操作对象自身的功能

        实例方法是对象自己的功能,必须依赖具体对象才能调用,能访问实例属性和类属性。

  • 定义方式

        第一个参数必须是self(代表当前对象)。

  • 调用

        实例.实例方法名(调用传参)
                    或
        类名.实例方法名(实例, 调用传参)

class 类名(继承列表):
    def 实例方法名(self, 参数1, 参数2, ...):
        "文档字符串"
        语句块

示例1:

class Dog:
    def __init__(self, name):
        self.name = name  # 实例属性

    # 实例方法:狗叫(依赖具体对象的名字)
    def bark(self):
        print(f"{self.name}:汪汪!")  # 访问实例属性self.name

    # 实例方法:吃食物(带参数)
    def eat(self, food):
        print(f"{self.name}在吃{food}")

# 创建对象
dog = Dog("旺财")

# 调用实例方法:对象名.方法名()
dog.bark()  # 输出:旺财:汪汪!
dog.eat("骨头")  # 输出:旺财在吃骨头

         使用场景:需要操作对象自身属性的功能(如人的 "自我介绍"、手机的 "拨打电话")。

示例2:

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

help(Dog)  # 可以看到Dog类的文档信息

2.2.2 类方法:操作类属性的功能

  • 类方法属于类本身,不依赖具体对象,主要用于操作类属性。

  • 说明

    • 类方法需要使用@classmethod装饰器定义

    • 类方法至少有一个形参用于绑定类,约定为 $cls$

    • 类和该类的实例都可以调用类方法

    • 类方法不能访问此类创建的对象的实例属

  • 示例1

class School:
    # 类属性:学生总数
    total_students = 0

    # 类方法:增加学生数量(操作类属性)
    @classmethod
    def add_student(cls):  # cls代表School类
        cls.total_students += 1  # 修改类属性

    # 类方法:获取学生总数
    @classmethod
    def get_total(cls):
        return cls.total_students

# 调用类方法:类名.方法名()(不需要创建对象)
School.add_student()  # 增加1个学生
School.add_student()  # 再增加1个
print(School.get_total())  # 输出:2
  • 示例2

class A:
    v = 0

    @classmethod
    def set_v(cls, value):
        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())
  • 示例3
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}")


# 创建类的实例
obj = MyClass(5)

# 调用类方法修改类属性
MyClass.modify_class_attr(20)  # 输出: 类属性已修改为: 20
MyClass.try_modify_instance_attr()	# 事出反常必有妖

        使用场景:需要修改或查询类属性的功能(如统计类的实例数量、批量修改共性参数)。 

2.2.3 静态方法:类中的 "工具函数"

  • 静态方法定义在类的内部,作用域是类内部

  • 说明

    • 使用@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

示例2:

class Calculator:
    # 静态方法:计算两个数的和(纯工具功能)
    @staticmethod
    def add(a, b):
        return a + b

    # 静态方法:计算两个数的积
    @staticmethod
    def multiply(a, b):
        return a * b

# 调用静态方法:类名.方法名() 或 对象名.方法名()
print(Calculator.add(2, 3))  # 输出:5
calc = Calculator()
print(calc.multiply(4, 5))   # 输出:20

 

2.5.1 综合应用

小案例:

class BankAccount:
    interest_rate = 0.05  # 类属性

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

    def apply_interest(self):  # 实例方法
        self.balance *= (1 + self.interest_rate)

    @classmethod
    def set_interest_rate(cls, rate):  # 类方法
        cls.interest_rate = rate

    @staticmethod
    def validate_amount(amount):  # 静态方法
        return amount > 0  # 只是个工具方法,不依赖实例/类

# 使用类方法修改利率
BankAccount.set_interest_rate(0.07)

# 创建账户
acc = BankAccount(1000)
acc.apply_interest()
print(acc.balance)  # 1070.0

# 使用静态方法验证金额
print(BankAccount.validate_amount(-500))  # False

使用场景:逻辑上属于类,但不依赖类或对象的工具功能(如数学计算、数据验证)。 

3.构造与初始化:对象的 "出生过程"

        创建对象时,Python 会自动执行两个特殊方法:__new__(造对象)和__init__(初始化属性)。

3.1 __new__:创建对象的 "产房"

构造方法__new__()

  • 负责对象的 创建 和内存分配的。

  • 在对象实例化时被调用,负责返回一个新的对象实例。

  • 通常不需要显式地定义 __new__() 方法,Python会调用基类 $object$ 的 __new__() 方法。

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("调用 __new__ 方法,创建对象")
        return super().__new__(cls)

    def __init__(self, value):
        print("调用 __init__ 方法,初始化对象")
        self.value = value


# 创建对象
obj = MyClass(10)

3.2初始化方法__init__:给对象 "装零件"

  • 一旦对象被创建,Python会调用 __init__() 来初始化对象的属性。

  • 必须有self参数,代表刚创建的对象。
  • 可以定义其他参数,创建对象时传入。
  • 语法格式:

class 类名(继承列表):
    def __init__(self[, 形参列表]):
        语句块
# [] 代表其中的内容可省略
  • 接收实参到 __init__方法中

  • 代码示例:

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("调用 __new__ 方法,创建对象")
        return super().__new__(cls)

    def __init__(self, value):
        print("调用 __init__ 方法,初始化对象")
        self.value = value

# 创建对象
obj = MyClass(10)
'''
调用 __new__ 方法,创建对象
调用 __init__ 方法,初始化对象
'''

4.魔术方法:让对象 "懂规矩" 的特殊技能

        魔术方法是一种特殊的方法,用双下划线包裹,例如__init____str____add__等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。

4.1 __str__:自定义对象的 "自我介绍"

        当用print()str()输出对象时,会自动调用__str__,返回一个用户友好的字符串。

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

    # 自定义打印格式
    def __str__(self):
        return f"我叫{self.name},今年{self.age}岁"

p = Person("小明", 18)
print(p)  # 输出:我叫小明,今年18岁(自动调用__str__)
print(str(p))  # 输出:我叫小明,今年18岁(同样调用__str__)

4.2 __repr__:给开发者看的 "官方说明"

        当用repr()查看对象时,会调用__repr__,返回一个能重建对象的字符串(通常是代码形式)。

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

    # 开发者友好的格式(可直接复制创建对象)
    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

p = Person("小红", 20)
print(repr(p))  # 输出:Person('小红', 20)(适合调试)

小技巧:如果没定义__str__print()会用__repr__的结果。

4.3 __add__:让对象支持 "+" 运算

        自定义两个对象相加的逻辑,用+时自动调用。

class Point:
    def __init__(self, x, y):
        self.x = x  # x坐标
        self.y = y  # y坐标

    # 自定义加法:两个点的x、y分别相加
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    # 配合打印,定义__repr__
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2  # 自动调用__add__
print(p3)  # 输出:Point(4, 6)

4.4 __sub__:让对象支持 "-" 运算

        类似__add__,自定义减法逻辑。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # 自定义减法
    def __sub__(self, other):
        return Point(self.x - other.x, self.y - other.y)

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(5, 5)
p2 = Point(2, 1)
print(p1 - p2)  # 输出:Point(3, 4)

4.5 __len__:让对象支持len()函数

        自定义len()的返回值,通常用于表示对象的 "长度"。

class MyList:
    def __init__(self, items):
        self.items = items  # 存储列表数据

    # 自定义len()的结果为列表长度
    def __len__(self):
        return len(self.items)

my_list = MyList([1, 2, 3, 4])
print(len(my_list))  # 输出:4(自动调用__len__)

4.6 __getitem__:让对象支持 "[]" 索引

        自定义通过索引(如obj[0])访问对象的逻辑。

class MyList:
    def __init__(self, items):
        self.items = items

    # 自定义索引访问:返回对应位置的元素
    def __getitem__(self, index):
        return self.items[index]

my_list = MyList([10, 20, 30])
print(my_list[1])  # 输出:20(自动调用__getitem__)

4.7 __setitem__:让对象支持 "[]=" 赋值

        自定义通过索引(如obj[0] = 5)修改对象的逻辑。

class MyList:
    def __init__(self, items):
        self.items = items

    def __setitem__(self, index, value):
        self.items[index] = value  # 修改对应位置的元素

my_list = MyList([10, 20, 30])
my_list[1] = 200  # 自动调用__setitem__
print(my_list.items)  # 输出:[10, 200, 30]

4.8 __delitem__:让对象支持 "del" 删除

        自定义通过del obj[0]删除元素的逻辑。

class MyList:
    def __init__(self, items):
        self.items = items

    def __delitem__(self, index):
        del self.items[index]  # 删除对应位置的元素

my_list = MyList([10, 20, 30])
del my_list[0]  # 自动调用__delitem__
print(my_list.items)  # 输出:[20, 30]

4.9 __iter____next__:让对象支持 for 循环

        实现这两个方法,对象就可以被 for 循环遍历(成为迭代器)。

class Counter:
    def __init__(self, end):
        self.end = end  # 计数终点
        self.current = 0  # 当前计数

    # 迭代器必须返回自己
    def __iter__(self):
        return self

    # 每次迭代返回下一个值
    def __next__(self):
        if self.current < self.end:
            self.current += 1
            return self.current
        else:
            # 停止迭代
            raise StopIteration

# 用for循环遍历Counter对象
for num in Counter(3):
    print(num)  # 输出:1 → 2 → 3

4.10 __eq__:自定义 "==" 比较逻辑

        默认==比较对象的内存地址,用__eq__可自定义比较规则。

class Student:
    def __init__(self, id, name):
        self.id = id  # 学号(唯一标识)
        self.name = name

    # 自定义相等:学号相同则认为相等
    def __eq__(self, other):
        return self.id == other.id

s1 = Student(1001, "小明")
s2 = Student(1001, "小红")  # 学号相同
s3 = Student(1002, "小明")  # 学号不同

print(s1 == s2)  # 输出:True(学号相同)
print(s1 == s3)  # 输出:False(学号不同)

4.11 __lt____gt__:自定义 "<" 和 ">" 比较

        __lt__定义小于逻辑,__gt__定义大于逻辑。

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

    # 自定义小于:价格小则认为小
    def __lt__(self, other):
        return self.price < other.price

    # 自定义大于:价格大则认为大
    def __gt__(self, other):
        return self.price > other.price

p1 = Product(100)
p2 = Product(200)

print(p1 < p2)  # 输出:True(100 < 200)
print(p1 > p2)  # 输出:False(100 > 200不成立)

4.12 __call__:让对象像函数一样被调用

        定义__call__后,对象可以用对象名()的形式调用。

class Adder:
    def __init__(self, num):
        self.num = num  # 基础数字

    # 让对象可调用:传入x,返回x + num
    def __call__(self, x):
        return x + self.num

add5 = Adder(5)  # 创建一个"加5"的对象
print(add5(3))   # 输出:8(相当于调用函数,3+5)
print(add5(10))  # 输出:15(10+5)

5、实战练习:巩固类和对象的知识

设计一个 "图书" 类

  • 实例属性:书名(name)、作者(author)、页数(pages)
  • 类属性:类别(category = "书籍")
  • 实例方法:get_info() 打印图书信息
  • 类方法:set_category(new_cat) 修改类别
  • 魔术方法:__str__ 自定义打印格式

class Book:
    category = '书籍'

    def __init__(self, name, autor, pages):
        """
             初始化图书对象

             参数:
                 name: 书名
                 author: 作者
                 pages: 页数
        """
        self.name = name
        self.autor = autor
        self.pages = pages

    def get_info(self):
        """返回格式化的图书信息字符串"""
        return f'书名:{self.name}, 作者:{self.autor}, 页数{self.pages}'

    @classmethod
    def set_category(cls, new_cat):
        """修改图书类别"""
        cls.category = new_cat

    def __str__(self):
        """返回图书的简短描述"""
        return f'{self.name} ({self.autor}著)'


book = Book("Python 编程", "张三", 300)
print(book)     # 输出:Python 编程(张三 著)
book.get_info()     # 输出:书名:Python 编程...
print(Book.category)   # 输出:书籍
Book.set_category("技术图书")
print(book.category)    # 输出:技术图书

总结:类和对象是编程的 "积木"

        类和对象是面向对象编程的核心,它们让代码更贴近现实世界的逻辑:

  • 用类定义 "模板",用对象创建 "实物";
  • 用属性描述特征,用方法定义功能;
  • 用魔术方法让对象支持 Python 的内置功能,更 "合群"。

        学会类和对象后,你可以用更模块化、更易维护的方式编写复杂程序 —— 就像用积木搭房子,先做好基础零件(类),再组合出各种造型(程序)。


网站公告

今日签到

点亮在社区的每一天
去签到