@classmethod和@staticmethod对初学者的意义?
有人可以向我解释@classmethod
和@staticmethod
在python中的含义吗? 我需要知道其中的差异和意义。
据我所知, @classmethod
告诉一个类,它是一种方法应该被inheritance到子类,或…东西。 但是,这是什么意思? 为什么不定义类方法而不添加@classmethod
或@staticmethod
或任何@
定义?
tl;博士:我应该什么时候使用它们, 为什么要使用它们,我应该如何使用它们?
我用C ++很高级,所以使用更高级的编程概念应该不成问题。 如果可能,请随意给我一个相应的C ++示例。
虽然classmethod
和staticmethod
非常相似,但两个实体的使用情况略有不同: classmethod
必须对类对象的引用作为第一个参数,而staticmethod
根本没有参数。
让我们看看实际例子中所说的一切。
样板
让我们假设一个类的例子,处理date信息(这将是我们的样板烹饪):
class Date(object): def __init__(self, day=0, month=0, year=0): self.day = day self.month = month self.year = year
这个类显然可以用来存储有关某些date的信息(没有时区信息;假设所有date都以UTC表示)。
在这里,我们有__init__
,一个Python类实例的典型初始化方法,它接收参数作为一个典型的instancemethod
,拥有第一个非可选参数( self
),它持有对新创build实例的引用。
类方法
我们有一些使用classmethod
可以很好地完成的任务。
假设我们想创build大量的Date
类实例,它们将来自外部源的date信息编码为下一个格式('dd-mm-yyyy')的string。 我们必须在我们的项目源代码的不同地方这样做。
所以我们在这里必须做的是:
- 将stringparsing为接收日,月和年作为三个整数variables或由该variables组成的三项目元组。
- 通过将这些值传递给初始化调用来实例化
Date
。
这看起来像:
day, month, year = map(int, string_date.split('-')) date1 = Date(day, month, year)
为此,C ++具有重载等特性,但是Python缺less这个特性 – 所以这里是classmethod
应用。 让我们创build另一个“ 构造函数 ”。
@classmethod def from_string(cls, date_as_string): day, month, year = map(int, date_as_string.split('-')) date1 = cls(day, month, year) return date1 date2 = Date.from_string('11-09-2012')
让我们仔细看看上面的实现,并回顾一下我们在这里有什么优势:
- 我们已经在一个地方实现了datestringparsing,现在它是可重用的。
- 封装在这里工作得很好(如果你认为你可以在别处实现stringparsing作为一个单独的函数,这个解决scheme更适合OOP范例)。
-
cls
是持有类本身的对象,而不是类的实例。 这很酷,因为如果我们inheritance我们的Date
类,所有的孩子也会有from_string
定义。
静态方法
staticmethod
呢? 它和classmethod
非常相似,但不包含任何强制性参数(如类方法或实例方法)。
我们来看下一个用例。
我们有一个我们想要以某种方式validation的datestring。 这个任务也逻辑上绑定到我们迄今使用的Date
类,但是仍然不需要实例化它。
这里staticmethod
可以是有用的。 我们来看下一段代码:
@staticmethod def is_date_valid(date_as_string): day, month, year = map(int, date_as_string.split('-')) return day <= 31 and month <= 12 and year <= 3999 # usage: is_date = Date.is_date_valid('11-09-2012')
所以,从staticmethod
使用中我们可以看到,我们没有任何访问类的东西 – 它基本上只是一个函数,在语法上就像一个方法一样,但是不能访问对象,它的内部(字段和另一个方法),而classmethod呢。
Rostyslav Dzinko的回答非常合适。 我想我可以突出显示另一个原因,当你创build额外的构造函数时,你应该select@classmethod
over @staticmethod
。
在上面的例子中,Rostyslav使用@classmethod
from_string
作为Factory来从不可接受的参数创buildDate
对象。 如下面的代码所示, @staticmethod
也可以做同样的事情:
class Date: def __init__(self, month, day, year): self.month = month self.day = day self.year = year def display(self): return "{0}-{1}-{2}".format(self.month, self.day, self.year) @staticmethod def millenium(month, day): return Date(month, day, 2000) new_year = Date(1, 1, 2013) # Creates a new Date object millenium_new_year = Date.millenium(1, 1) # also creates a Date object. # Proof: new_year.display() # "1-1-2013" millenium_new_year.display() # "1-1-2000" isinstance(new_year, Date) # True isinstance(millenium_new_year, Date) # True
因此, new_year
和new_year
都是Date
类的实例。
但是,如果仔细观察,Factory进程是硬编码的,无论如何创buildDate
对象。 这意味着即使Date
类是子类,子类仍然会创build普通的Date
对象(没有子类的任何属性)。 请看下面的例子:
class DateTime(Date): def display(self): return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year) datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # False datetime1.display() # returns "10-10-1990 - 00:00:00PM" datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class
datetime2
不是一个DateTime
的实例? WTF? 那是因为使用了@staticmethod
装饰器。
在大多数情况下,这是不受欢迎的。 如果你想要的是一个Factory方法,它知道调用它的类,那么@classmethod
就是你所需要的。
将Date.millenium
重写为(这是上面代码的唯一改变部分)
@classmethod def millenium(cls, month, day): return cls(month, day, 2000)
确保class
不是硬编码的,而是学到的。 cls
可以是任何子类。 得到的object
将正确地成为cls
一个实例。 我们来testing一下。
datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # True datetime1.display() # "10-10-1990 - 00:00:00PM" datetime2.display() # "10-10-2000 - 00:00:00PM"
原因是,就像你现在知道的那样, @classmethod
被用来代替@staticmethod
@classmethod
意思是:当这个方法被调用的时候,我们传递这个类作为第一个参数,而不是那个类的实例(就像我们通常用的方法一样)。 这意味着您可以在该方法内使用该类及其属性,而不是特定的实例。
@staticmethod
意思是:当这个方法被调用的时候,我们不会把类的实例传递给它(就像我们通常用的方法一样)。 这意味着你可以在一个类中放置一个函数,但是你不能访问那个类的实例(当你的方法不使用实例的时候,这是很有用的)。
@staticmethod
函数不过是一个类中定义的函数。 它可以被调用,而不需要先实例化类。 它的定义是通过inheritance而不可变的。
- Python不必为对象实例化绑定方法 。
- 它简化了代码的可读性:看到@staticmethod ,我们知道该方法不依赖于对象本身的状态;
@classmethod
函数也可以在没有实例化类的情况下调用,但是它的定义如下Sub类,而不是Parent类,通过inheritance,可以被子类覆盖。 这是因为@classmethod
函数的第一个参数必须始终是cls (class)
。
- 工厂方法 ,用于使用例如某种预处理来为类创build实例。
- 调用静态方法的静态方法 :如果在多个静态方法中分离静态方法,则不应该对类名称进行硬编码,而应使用类方法
这里是这个主题的好链接。
当他/她想要根据哪个子类正在调用方法来改变方法的行为时,可以使用@classmethod
。 记得我们有一个类方法中的调用类的引用。
在使用静态的时候,你希望行为在子类中保持不变
例:
class Hero: @staticmethod def say_hello(): print("Helllo...") @classmethod def say_class_hello(cls): if(cls.__name__=="HeroSon"): print("Hi Kido") elif(cls.__name__=="HeroDaughter"): print("Hi Princess") class HeroSon(Hero): def say_son_hello(self): print("test hello") class HeroDaughter(Hero): def say_daughter_hello(self): print("test hello daughter") testson = HeroSon() testson.say_class_hello() #Output: "Hi Kido" testson.say_hello() #Outputs: "Helllo..." testdaughter = HeroDaughter() testdaughter.say_class_hello() #Outputs: "Hi Princess" testdaughter.say_hello() #Outputs: "Helllo..."
有点汇编
@staticmethod一种在类中编写方法的方法,不需要引用被调用的对象。 所以不需要像self或cls那样传递隐含的参数。 它是写在外面类如何写,但它是没有用的python,因为如果你需要封装一个方法内的类,因为这个方法需要是该类的一部分@staticmethod是方便的案件。
@classmethod当你想编写一个工厂方法时,这是很重要的,并且这个自定义属性可以被附加到一个类中。 这个属性可以在inheritance的类中重写。
这两种方法的比较如下
@classmethod
和@staticmethod
含义?
- 方法是对象的名称空间中的一个函数,可以作为属性访问。
- 常规(即实例)方法获取实例(我们通常将其称为
self
)作为隐含的第一个参数。 - 一个类方法获取类(我们通常称之为
cls
)作为隐式的第一个参数。 - 一个静态方法没有隐式的第一个参数(像一个常规函数)。
我应该什么时候使用它们?为什么要使用它们?我应该如何使用它们?
你不需要任何装饰。 但是,你应该尽量减less函数参数的数量(参见Clean Coder),这对于做这件事很有用。
class Example(object): def regular_instance_method(self): """A function of an instance has access to every attribute of that instance, including its class (and its attributes.) Not accepting at least one argument is a TypeError. Not understanding the semantics of that argument is a user error. """ return some_function_f(self) @classmethod def a_class_method(cls): """A function of a class has access to every attribute of the class. Not accepting at least one argument is a TypeError. Not understanding the semantics of that argument is a user error. """ return some_function_g(cls) @staticmethod def a_static_method(): """A static method has no information about instances or classes unless explicitly given. It just lives in the class (and thus its instances') namespace. """ return some_function_h()
对于实例方法和类方法,不接受至less一个参数是TypeError,但不理解该参数的语义是用户错误。
(定义some_function
的,例如:
some_function_h = some_function_g = some_function_f = lambda x=None: x
这将工作。)
对实例和类进行虚线查找:
一个实例上的虚线查找按这个顺序执行 – 我们寻找:
- 类名称空间中的数据描述符(如属性)
- 实例中的数据
__dict__
- 类名称空间(方法)中的非数据描述符。
请注意,实例上的虚线查找是这样调用的:
instance = Example() instance.regular_instance_method
和方法是可调用的属性:
instance.regular_instance_method()
实例方法
这个论点, self
,是通过虚线的查询隐含给出的。
您必须从类的实例中访问实例方法。
>>> instance = Example() >>> instance.regular_instance_method() <__main__.Example object at 0x00000000399524E0>
类方法
参数cls
隐含地通过虚线查找给出。
您可以通过实例或类(或子类)来访问此方法。
>>> instance.a_class_method() <class '__main__.Example'> >>> Example.a_class_method() <class '__main__.Example'>
静态方法
没有任何参数是隐含给出的。 这个方法的作用就像在模块名字空间中定义的任何函数一样,除了它可以被查找
>>> print(instance.a_static_method()) None
再次,我应该什么时候使用它们,我为什么要使用它们?
这些方法中的每一个在它们传递方法和实例方法的信息方面越来越严格。
当你不需要这些信息时使用它们。
这使得你的函数和方法更容易推理和unit testing。
哪个更容易推理?
def function(x, y, z): ...
要么
def function(y, z): ...
要么
def function(z): ...
参数较less的函数更容易推理。 他们也更容易进行unit testing。
这些类似于实例,类和静态方法。 记住,当我们有一个实例,我们也有它的课,再问问自己,这是更容易推理?
def an_instance_method(self, cls, arg, kwarg=None): ... @classmethod def a_class_method(cls, arg, kwarg=None): ... @staticmethod def a_static_method(arg, kwarg=None): ...
内置的例子
以下是我最喜欢的一些内置示例:
str.maketrans
静态方法是string
模块中的一个函数,但是从str
命名空间访问它更方便。
>>> 'abc'.translate(str.maketrans({'a': 'b'})) 'bbc'
dict.fromkeys
类方法返回一个从一个可迭代的键实例化的新字典:
>>> dict.fromkeys('abc') {'a': None, 'c': None, 'b': None}
当子类化时,我们看到它将类信息作为类方法获得,这非常有用:
>>> class MyDict(dict): pass >>> type(MyDict.fromkeys('abc')) <class '__main__.MyDict'>
我的build议 – 结论
当你不需要类或者实例参数时,使用静态方法,但是这个函数和对象的使用有关,并且方便这个函数在对象的名字空间中。
当你不需要实例信息时,可以使用类方法,但是可能需要类信息用于其他类或静态方法,或者也可以将其本身作为构造函数。 (你不会硬编码这个类,所以这里可以使用子类。)
一种稍微不同的方式来思考它可能对某人有用…在超类中使用一个类方法来定义当不同的子类调用时该方法应该如何performance。 当我们想要返回相同的东西时,使用静态方法,而不pipe我们调用的子类是什么。
我是这个网站的初学者,我已经阅读了所有以上的答案,并得到了我想要的信息。 但是,我没有权利上传。 所以我想用StackOverflow来开始我的理解。
-
@staticmethod
不需要self或cls作为方法的第一个参数 -
@staticmethod
和@classmethod
包装函数可以被实例或类variables调用 -
@staticmethod
装饰函数会影响子类inheritance的某种“不可变属性”不能覆盖由@staticmethod
装饰器包装的基类函数。 -
@classmethod
需要cls(类名称,如果需要可以更改variables名称,但不build议)作为函数的第一个参数 -
@classmethod
总是以子类方式使用,子类inheritance可能会改变基类函数的效果,即@classmethod
包装的基类函数可以被不同的子类覆盖。
类方法可以修改类的状态,它绑定到类,它包含cls作为参数。
静态方法不能修改类的状态,它绑定到类,它不知道类或实例
class empDetails: def __init__(self,name,sal): self.name=name self.sal=sal @classmethod def increment(cls,name,none): return cls('yarramsetti',6000 + 500) @staticmethod def salChecking(sal): return sal > 6000 emp1=empDetails('durga prasad',6000) emp2=empDetails.increment('yarramsetti',100) # output is 'durga prasad' print emp1.name # output put is 6000 print emp1.sal # output is 6500,because it change the sal variable print emp2.sal # output is 'yarramsetti' it change the state of name variable print emp2.name # output is True, because ,it change the state of sal variable print empDetails.salChecking(6500)