1532 python oop static-methods class-method
有人可以向我解释@classmethod和@staticmethodpython中的含义吗?我需要知道差异和意义.
据我所知,@classmethod告诉一个类,它是一个应该继承到子类的方法,或者......某种东西.但是,重点是什么?为什么不在不添加@classmethod或定义@staticmethod任何@定义的情况下定义类方法?
tl; dr: 我应该何时使用它们,为什么要使用它们,我应该如何使用它们?
我在C++方面非常先进,所以使用更高级的编程概念应该不是问题.如果可能的话,请随意给我一个相应的C++示例.
Ros*_*nko 2518
虽然classmethod并且staticmethod非常相似,但两个实体的使用情况略有不同:classmethod必须将类对象的引用作为第一个参数,而staticmethod根本没有参数.
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
@classmethod
def from_string(cls, date_as_string):
day, month, year = map(int, date_as_string.split('-'))
date1 = cls(day, month, year)
return date1
@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
date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')
Run Code Online (Sandbox Code Playgroud)
让我们假设一个类的例子,处理日期信息(这将是我们的样板):
class Date(object):
def __init__(self, day=0, month=0, year=0):
self.day = day
self.month = month
self.year = year
Run Code Online (Sandbox Code Playgroud)
这个类显然可以用来存储关于某些日期的信息(没有时区信息;我们假设所有日期都以UTC表示).
这里我们有__init__一个Python类实例的典型初始化器,它接收作为典型的参数instancemethod,具有第一个非可选参数(self),它包含对新创建的实例的引用.
类方法
我们有一些任务可以很好地使用classmethods 完成.
让我们假设我们想创建许多Date类实例,其中日期信息来自外部源,编码为格式为'dd-mm-yyyy'的字符串.假设我们必须在项目源代码的不同位置执行此操作.
所以我们必须做的是:
Date通过将这些值传递给初始化调用来实例化.这看起来像:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)
Run Code Online (Sandbox Code Playgroud)
为此,C++可以通过重载实现这样的功能,但是Python缺少这种重载.相反,我们可以使用classmethod.让我们创建另一个" 构造函数 ".
@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')
Run Code Online (Sandbox Code Playgroud)
让我们更仔细地看一下上面的实现,并回顾一下我们在这里有什么优势:
cls是一个持有类本身的对象,而不是类的实例.这很酷,因为如果我们继承我们的Date班级,所有孩子也会from_string定义.静态方法
怎么样staticmethod?它非常相似classmethod但不采用任何强制性参数(如类方法或实例方法).
我们来看看下一个用例.
我们有一个日期字符串,我们想以某种方式验证.此任务在逻辑上也绑定到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')
Run Code Online (Sandbox Code Playgroud)
因此,正如我们从使用中看到的那样staticmethod,我们无法访问类的内容 - 它基本上只是一个函数,在语法上称为方法,但无法访问对象及其内部(字段和另一个)方法),而classmethod.
Yaw*_*kye 829
Rostyslav Dzinko的回答非常合适.我以为我可以强调你应该选择一个其他原因@classmethod了@staticmethod,当你创建额外的构造函数.
在上面的示例中,Rostyslav使用@classmethod from_string作为工厂Date从其他不可接受的参数创建对象.可以使用@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
Run Code Online (Sandbox Code Playgroud)
因此,这两个new_year和millenium_new_year的实例Date类.
但是,如果仔细观察,工厂流程就会被硬编码以创建Date对象,无论如何.这意味着即使Date类是子类,子类仍将创建普通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 for more details.
Run Code Online (Sandbox Code Playgroud)
datetime2不是一个实例DateTime?WTF?那是因为使用了@staticmethod装饰器.
在大多数情况下,这是不希望的.如果你想要的是一个知道调用它的类的Factory方法,那么@classmethod你就需要它.
重写Date.milleniumas(这是上面代码中唯一改变的部分)
@classmethod
def millenium(cls, month, day):
return cls(month, day, 2000)
Run Code Online (Sandbox Code Playgroud)
确保class不是硬编码而是学习.cls可以是任何子类.结果object将正确地成为.的实例cls.让我们测试一下.
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"
Run Code Online (Sandbox Code Playgroud)
原因是,正如你现在所知,@classmethod被用来代替@staticmethod
Sim*_*ser 240
@classmethod意味着:当调用此方法时,我们将类作为第一个参数而不是该类的实例传递(正如我们通常使用的方法).这意味着您可以在该方法中使用类及其属性,而不是特定实例.
@staticmethod意味着:当调用此方法时,我们不会将类的实例传递给它(正如我们通常使用的方法).这意味着您可以将一个函数放在一个类中,但是您无法访问该类的实例(当您的方法不使用该实例时,这很有用).
zan*_*ngw 76
@staticmethod函数只不过是在类中定义的函数.它可以在不首先实例化类的情况下调用.它的定义是通过继承不可变的.
@classmethod函数也可以在不实例化类的情况下调用,但是它的定义遵循Sub类,而不是Parent类,通过继承,可以被子类覆盖.那是因为@classmethod函数的第一个参数必须始终是cls (class).
这里是这个主题的好链接.
Aar*_*all 43
意义
@classmethod和@staticmethod?
self)作为隐式的第一个参数.cls)作为隐含的第一个参数.我何时应该使用它们,为什么要使用它们,我应该如何使用它们?
你不需要装饰器.但是根据你应该最小化函数参数数量的原则(参见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()
Run Code Online (Sandbox Code Playgroud)
对于实例方法和类方法,不接受至少一个参数是TypeError,但不理解该参数的语义是用户错误.
(定义some_function,例如:
some_function_h = some_function_g = some_function_f = lambda x=None: x
Run Code Online (Sandbox Code Playgroud)
这将有效.)
按顺序执行实例上的虚线查找 - 我们查找:
__dict__注意,对实例的虚线查找调用如下:
instance = Example()
instance.regular_instance_method
Run Code Online (Sandbox Code Playgroud)
和方法是可调用的属性:
instance.regular_instance_method()
Run Code Online (Sandbox Code Playgroud)
参数,self是通过点缀查找隐式给出的.
您必须从类的实例访问实例方法.
>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>
Run Code Online (Sandbox Code Playgroud)
该参数cls是通过点查找隐式给出的.
您可以通过实例或类(或子类)访问此方法.
>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>
Run Code Online (Sandbox Code Playgroud)
没有隐含地给出任何参数.此方法的工作方式与模块命名空间中定义的任何函数(例如)类似,但可以查找它
>>> print(instance.a_static_method())
None
Run Code Online (Sandbox Code Playgroud)
我应该何时使用它们,为什么要使用它们呢?
这些中的每一个在通过方法与实例方法的信息中逐渐受到更多限制.
在不需要信息时使用它们.
这使您的功能和方法更容易推理和单元测试.
哪个更容易推理?
def function(x, y, z): ...
Run Code Online (Sandbox Code Playgroud)
要么
def function(y, z): ...
Run Code Online (Sandbox Code Playgroud)
要么
def function(z): ...
Run Code Online (Sandbox Code Playgroud)
参数较少的函数更容易推理.它们也更容易进行单元测试.
这些类似于实例,类和静态方法.请记住,当我们有一个实例时,我们也有它的类,再次问自己,哪个更容易推理?:
def an_instance_method(self, arg, kwarg=None):
cls = type(self) # Also has the class of instance!
...
@classmethod
def a_class_method(cls, arg, kwarg=None):
...
@staticmethod
def a_static_method(arg, kwarg=None):
...
Run Code Online (Sandbox Code Playgroud)
这里有几个我最喜欢的内置示例:
该str.maketrans静态方法是在一个功能string模块,但它更方便它是从访问的str命名空间.
>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'
Run Code Online (Sandbox Code Playgroud)
本dict.fromkeys类方法返回从键的迭代实例化一个新的字典:
>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}
Run Code Online (Sandbox Code Playgroud)
当子类化时,我们看到它将类信息作为类方法获取,这非常有用:
>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'>
Run Code Online (Sandbox Code Playgroud)
当您不需要类或实例参数时,请使用静态方法,但该函数与对象的使用相关,并且函数位于对象的命名空间中很方便.
当您不需要实例信息时使用类方法,但可能需要类信息用于其他类或静态方法,或者可能本身作为构造函数.(您不会对类进行硬编码,以便可以在此处使用子类.)
小智 36
@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..."
Run Code Online (Sandbox Code Playgroud)
SIs*_*lam 33
一点汇编
@staticmethod 一种在类中编写方法而不引用它所调用的对象的方法.所以不需要传递像self或cls这样的隐式参数.它的写法与在类外写的完全相同,但它在python中没有用,因为如果你需要在一个类中封装一个方法,因为这个方法需要成为该类的一部分@staticmethod就派上用了案件.
@classmethod 当你想编写一个工厂方法时,这很重要,并且这个自定义属性可以附加在一个类中.可以在继承的类中重写此属性.
这两种方法之间的比较如下
| 归档时间: |
|
| 查看次数: |
593888 次 |
| 最近记录: |