赞
踩
1. 概览
先定义一个最简单的 Python 3 的类:
class MyClass:
def method(self):
print('我是实例方法', self)
@classmethod
def classmethod(cls):
print('我是类方法', cls)
@staticmethod
def staticmethod():
print('我是静态方法')
1.1 实例方法
第一个方法 method(self) 方法是 实例方法 instance method 。 当 method 被调用时, self 参数指向 MyClass 类的一个实例。 实例方法可以通过 self 自由地访问同一对象的属性和其它方法,这样它们可以修改实例的状态。 注意实例方法可以通过 self.__class__
属性来获取到类,所以实例方法也可以更改类的状态。
1.2 类方法
第二个方法 classmethod(cls) 是 类方法 class method 。 上面需要写一个 @classmethod 装饰器。 类方法接收一个 cls 参数,当该方法被调用的时候,它指向类(而不是类的实例)。 类方法只有 cls 参数,所以它 不能 修改实例的状态。 修改实例的状态必须要有 self 参数。 类方法只能修改类的状态,类状态的更改会作用于所有该类的实例。
1.3 静态方法
第三个方法 staticmethod() 是 静态方法 static method 。 它上面要有一个 @staticmethod 装饰器。 静态方法不能修改类或者实例的状态,它受限于它所接收的参数。 我们一般用这种方法来隔离命名空间。
2. 实际应用
2.1 调用实例方法
首先创建一个实例,然后调用一下实例方法:
obj = MyClass()
# 调用实例方法
obj.method()
"""
我是实例方法 <__main__.MyClass object at 0x00000213E209B898>
"""
还可以这样调用:
MyClass.method(obj)
"""
我是实例方法 <__main__.MyClass object at 0x00000213E209B898>
"""
使用 对象.实例方法() 这种点号调用的形式是一个语法糖, Python 会自动把 对象 作为第一个实参,传递给 实例方法 中的 self 形参。 如果使用 类.实例方法(对象) 这种形式,则必须手动传递 对象 给 实例方法 的第一个参数 self 。
如果不创建实例就调用实例方法,或者是不传入 对象 ,那么就会出错:
# 不创建实例就调用实例方法会发生什么?
# 会提示缺少位置参数 self
# 实例方法依赖于实例而存在
MyClass.method()
"""
Traceback (most recent call last):
File "test.py", line 28, in <module>
MyClass.method()
TypeError: method() missing 1 required positional argument: 'self'
"""
实例方法可以通过 self.__class__
访问到类。
# 打印类名
print(obj.__class__.__name__)
"""
MyClass
"""
2.2 调用类方法
下面来调用一下类方法。
# 通过类名调用类方法
MyClass.classmethod()
# 也会自动传递类名作为第一个参数
"""
我是类方法 <class '__main__.MyClass'>
"""
通过 类.类方法() 的形式调用类方法, Python 会自动把 类 作为第一个参数传递给 类方法 的第一个参数 cls ,我们不用手动传递。
也可以用实例调用类方法:
# 当然也可以通过实例调用类方法
obj.classmethod()
"""
我是类方法 <class '__main__.MyClass'>
"""
通过实例调用类方法, Python 会把该实例的类传递给 类方法 的 cls 参数,该实例的类未必是定义类方法的类。如下例:
''' 学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' # 父类 class Animal: @classmethod def classmethod(cls): print('cls是:' + str(cls.__name__)) # 子类 class Dog(Animal): pass dog = Dog() dog.classmethod() """ cls是:Dog """ # 注意不是类方法的定义类:Animal # 而是实例的所属类:Dog
2.3 调用静态方法
最后调用一下静态方法:
# 调用静态方法
obj.staticmethod()
"""
我是静态方法
"""
# 调用静态方法的时候
# 点号语法不会自动传递任何参数
通过 实例.静态方法() 调用静态方法的时候, Python 不会传递 self 和 cls ,以此来限制静态方法的权限。所以静态方法不能获取实例或者类的状态。 它们就像普通函数一样,只不过隶属于类和该类的每个实例的命名空间。
2.4 不创建实例调用方法
不创建实例,调用实例方法、类方法和静态方法。
# 不创建实例,调用类方法 MyClass.classmethod() """ 我是类方法 <class '__main__.MyClass'> """ # 不创建实例,调用静态方法 MyClass.staticmethod() """ 我是静态方法 """ # 不创建实例,调用实例方法 MyClass.method() """ Traceback (most recent call last): File "test.py", line 85, in <module> MyClass.method() TypeError: method() missing 1 required positional argument: 'self' """
不创建实例,调用实例方法出错。 这是可以理解的,因为我们直接通过类这个蓝图 blueprint 本身来调用实例方法, Python 无法给 self 传参。
3. 使用类方法实现披萨工厂
''' 学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' class Pizza: def __init__(self, ingredients): self.ingredients = ingredients def __repr__(self): return f'披萨({self.ingredients})' @classmethod def margherita(cls): return cls(['马苏里拉奶酪', '番茄']) @classmethod def prosciutto(cls): return cls(['马苏里拉奶酪', '番茄', '火腿'])
使用类方法作为工厂函数,生产不同种类的披萨。
【注】 工厂函数 factory function 工厂函数是一个函数,它根据不同的输入,新建并返回不同的对象。
注意在工厂函数中,没有直接使用 Pizza 这个类名,而是使用了 cls 这个参数。 这样的好处在于易于维护。 万一以后要把 Pizza 这个类名改成 披萨 ,只改动一处就行,因为类方法中用的是 cls 而不是直接写 类名 。 这是遵循 DRY 原则的一个小技巧( Don’t repeat yourself )
现在使用工厂函数来生成几个披萨吧:
pizza1 = Pizza.margherita()
print(pizza1)
"""
披萨(['马苏里拉奶酪', '番茄'])
"""
pizza2 = Pizza.prosciutto()
print(pizza2)
"""
披萨(['马苏里拉奶酪', '番茄', '火腿'])
"""
我们可以使用工厂函数来创建事先配置好的 Pizza 对象。 这些工厂函数内部都使用了 init 构造函数,它们提供了一个捷径,不用记忆各种披萨配方。 从另外一个角度来看,这些 类方法可以为一个类定义多个构造函数 。
4. 何时使用静态方法
改写上面写的 Pizza 类。
''' 学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' import math class Pizza: def __init__(self, radius, ingredients): self.radius = radius self.ingredients = ingredients def __repr__(self): return (f'披萨({self.radius!r}),' f'{self.ingredients!r})') def area(self): return self.circle_area(self.radius) @staticmethod def circle_area(r): return r ** 2 * math.pi
试一试使用静态方法:
# 生成披萨 p = Pizza(4, ['马苏里拉奶酪', '番茄']) print(p) """ 披萨(4,['马苏里拉奶酪', '番茄']) """ # 计算披萨的面积 p.area() print(p.area()) """ 50.26548245743669 """ # 通过类调用静态方法 print(Pizza.circle_area(4)) """ 50.26548245743669 """ # 通过对象调用静态方法 print(p.circle_area(4)) """ 50.26548245743669 """
把一个方法写成静态方法的好处:
5. 总结
调用实例方法,需要一个实例。实例方法可以通过 self 来获取实例。
self
self
类方法可以用实例或者类来调用。类方法可以通过 cls 获取类本身。类方法上面要加 @classmethod 装饰器。
静态方法可以用实例或者类调用。
静态方法无法获取到 cls 和 self 。 静态方法上面要加 @staticmethod 装饰器。
类方法和静态方法,从某种程度上传达了类的设计意图,使代码易于维护。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。