有人可以在python中向我解释 @classmethod
和 @staticmethod
的含义吗?我需要知道差异和意义 .
据我了解, @classmethod
告诉一个 class ,它的意思是_184409?为什么不在不添加 @classmethod
或 @staticmethod
或任何 @
定义的情况下定义类方法?
tl;dr: 何时应该使用它们,为什么要使用它们,我应该如何使用它们?
我对C非常先进,所以使用更高级的编程概念应该不是问题 . 如果可能,请随意给我一个相应的C示例 .
12 回答
类方法可以修改类状态,它绑定到类并且它包含cls作为参数 .
静态方法不能修改类状态,它绑定到类并且它不知道类或实例
稍微不同的思考方式可能对某人有用...在超类中使用类方法来定义该方法在被不同子类调用时应该如何表现 . 当我们想要返回相同的东西时使用静态方法,而不管我们调用的子类 .
虽然
classmethod
和staticmethod
非常相似,但两个实体的使用情况略有不同:classmethod
必须引用类对象作为第一个参数,而staticmethod
根本没有参数 .示例
解释
让我们假设一个类的例子,处理日期信息(这将是我们的样板):
这个类显然可以用来存储关于某些日期的信息(没有时区信息;我们假设所有日期都以UTC表示) .
这里我们有
__init__
,Python类实例的典型初始化器,它接收参数作为典型的instancemethod
,具有第一个非可选参数(self
),其中包含对新创建的实例的引用 .Class Method
我们有一些使用
classmethod
可以很好地完成的任务 .假设我们想要创建许多
Date
类实例,其日期信息来自外部源,编码为格式为'dd-mm-yyyy'的字符串 . 假设我们必须在项目源代码的不同位置执行此操作 .所以我们必须做的是:
将字符串解析为接收日,月和年作为三个整数变量或由该变量组成的3项元组 .
通过将这些值传递给初始化调用来实例化
Date
.这看起来像:
为此,C可以通过重载实现这样的功能,但是Python缺少这种重载 . 相反,我们可以使用
classmethod
. 让我们创建另一个“构造函数” .让我们更仔细地看一下上面的实现,并回顾一下我们在这里有什么优势:
我们've implemented date string parsing in one place and it'现在可以重复使用 .
封装在这里运行正常(如果你认为你可以在其他地方实现字符串解析作为单个函数,这个解决方案更适合OOP范例) .
cls
是一个保存 class itself 的对象,而不是该类的实例 . 这很酷,因为如果我们继承了Date
类,所有孩子也会定义from_string
.Static method
staticmethod
怎么样?它与classmethod
非常相似,但不采用任何强制性参数(如类方法或实例方法) .让我们看看下一个用例 .
我们有一个日期字符串,我们想要以某种方式验证 . 此任务也在逻辑上绑定到
Date
类,我们需要对其进行实例化 .这是
staticmethod
可能有用的地方 . 让我们看看下一段代码:因此,正如我们从
staticmethod
的使用中看到的那样,我们基本上只是一个函数,在语法上称为方法,但无法访问对象及其内部(字段和其他方法),而classmethod则可以 .Rostyslav Dzinko的回答非常合适 . 当我创建额外的构造函数时,我想我可以突出显示你应该选择
@classmethod
而不是@staticmethod
的另一个原因 .在上面的示例中,Rostyslav使用
@classmethod
from_string
作为工厂从其他不可接受的参数创建Date
对象 . 使用@staticmethod
可以完成同样的操作,如下面的代码所示:因此
new_year
和millenium_new_year
都是Date
类的实例 .但是,如果仔细观察,工厂流程将被硬编码以创建
Date
对象,无论如何 . 这意味着即使Date
类是子类,子类仍然会创建普通的Date
对象(没有子类的任何属性) . 请参阅以下示例中的内容:datetime2
不是DateTime
的实例? WTF?那是因为使用了@staticmethod
装饰器 .在大多数情况下,这是不希望的 . 如果您想要的是一个知道调用它的类的Factory方法,那么
@classmethod
就是您所需要的 .将
Date.millenium
重写为(这是上面代码中唯一改变的部分)确保
class
不是硬编码而是学习 .cls
可以是任何子类 . 结果object
将正确地成为cls
的实例 . 我们来试试吧 .原因是,如你所知,
@classmethod
被用来代替@staticmethod
A little compilation
@staticmethod 一种在类中编写方法而不引用它所调用的对象的方法 . 所以不需要传递像self或cls这样的隐式参数 . 它的写法与在类外写的完全相同,但它在python中没有用,因为如果你需要在一个类中封装一个方法,因为这个方法需要成为该类的一部分@staticmethod就派上用了案件 .
@classmethod 当您想要编写工厂方法并且此自定义属性可以附加到类中时,这很重要 . 可以在继承的类中重写此属性 .
这两种方法之间的比较如下
@classmethod
表示:当调用此方法时,我们将类作为第一个参数而不是该类的实例传递(正如我们通常使用的方法) . 这意味着您可以在该方法中使用类及其属性,而不是特定实例 .@staticmethod
表示:调用此方法时,我们不会访问该类的实例(当您的方法不使用该实例时,这很有用) .我是这个网站的初学者,我已经阅读了上述所有答案,并获得了我想要的信息 . 但是,我没有权利进行投票 . 所以我希望在StackOverflow上开始我的理解答案 .
@staticmethod
不需要self或cls作为方法的第一个参数@staticmethod
和@classmethod
包装函数可以通过实例或类变量调用@staticmethod
装饰函数影响某种'immutable property'该子类继承不能覆盖其由@staticmethod
装饰器包装的基类函数 .@classmethod
需要cls(类名,如果需要,可以更改变量名,但不建议)作为函数的第一个参数@classmethod
总是由子类方式使用,子类继承可能会改变基类函数的效果,即@classmethod
包装的基类函数可能会被不同的子类覆盖 .@classmethod
@classmethod
可与__init__
进行比较 . 你可能认为这是另一个__init__()
. 这是python在c中实现类构造函数重载的方式 .注意他们都有一个类的引用作为definitioin中的第一个参数,而
__init__
使用self
但construct_from_func
使用cls
传统 .@staticmethod
@staticmethod
可与object method
进行比较何时使用每个
@staticmethod
函数只不过是在类中定义的函数 . 它可以在不首先实例化类的情况下调用 . 它的定义是通过继承不可变的 .Python不必为对象实例化 bound-method .
它减轻了代码的可读性:看到 @staticmethod ,我们知道该方法不依赖于对象本身的状态;
@classmethod
函数也可以在不实例化类的情况下调用,但是它的定义遵循Sub类,而不是Parent类,通过继承,可以被子类覆盖 . 那是因为@classmethod
函数的第一个参数必须始终为cls (class)
.Factory methods ,用于为类创建实例,例如使用某种预处理 .
Static methods calling static methods :如果在几个静态方法中拆分静态方法,则不应对类名进行硬编码,而应使用类方法
here是这个主题的良好链接 .
当他/她想要根据调用方法的子类更改方法的行为时,可以使用
@classmethod
. 记住我们在类方法中引用了调用类 .使用静态时,您希望行为在子类中保持不变
例:
方法是对象命名空间中的一个函数,可作为属性访问 .
常规(即实例)方法获取实例(我们通常将其称为
self
)作为隐式的第一个参数 .class 方法获取类(我们通常称之为
cls
)作为隐式的第一个参数 .static 方法没有隐含的第一个参数(就像常规函数一样) .
你不需要装饰器 . 但是根据你应该最小化函数参数数量的原则(参见Clean Coder),它们对于这样做非常有用 .
对于实例方法和类方法,不接受至少一个参数是TypeError,但不理解该参数的语义是用户错误 .
(定义
some_function
,例如:这将有效 . )
点对实例和类的查找:
按顺序执行实例上的虚线查找 - 我们查找:
类名称空间中的数据描述符(如属性)
实例中的
数据
__dict__
类名称空间(方法)中的非数据描述符 .
注意,对实例的虚线查找调用如下:
和方法是可调用的属性:
实例方法
参数
self
是通过虚线查找隐式给出的 .您必须从类的实例访问实例方法 .
类方法
参数
cls
通过点查找隐式给出 .您可以通过实例或类(或子类)访问此方法 .
静态方法
没有隐含地给出任何参数 . 此方法的工作方式与模块命名空间中定义的任何函数(例如)类似,但可以查找它
这些中的每一个在通过方法与实例方法的信息中逐渐受到更多限制 .
在不需要信息时使用它们 .
这使您的功能和方法更容易推理和单元测试 .
哪个更容易推理?
要么
要么
参数较少的函数更容易推理 . 它们也更容易进行单元测试 .
这些类似于实例,类和静态方法 . 请记住,当我们有一个实例时,我们也有它的类,再次问自己,哪个更容易推理?:
内置示例
这里有几个我最喜欢的内置示例:
str.maketrans
静态方法是string
模块中的一个函数,但是从str
命名空间可以访问它更方便 .dict.fromkeys
类方法返回从可迭代键实例化的新字典:当子类化时,我们看到它将类信息作为类方法获取,这非常有用:
我的建议 - 结论
当您不需要类或实例参数时,请使用静态方法,但该函数与对象的使用相关,并且函数位于对象的命名空间中很方便 .
当您不需要实例信息时使用类方法,但是可能需要类信息用于其他类或静态方法,或者可能本身作为构造函数 . (您不会对类进行硬编码,以便可以在此处使用子类 . )
简而言之,@ classmehtod将普通方法转换为工厂方法 .
让我们用一个例子来探索它:
如果没有@ classmethod,你应该逐个创建实例并且它们是scartted .
例如@classmethod
测试一下:
看到?在类定义中成功创建实例,并将它们收集在一起 .
总之,@ classmethod decorator将传统方法转换为工厂方法,使用classmethods可以根据需要添加任意数量的替代构造函数 .