谁能给我解释一下python中
据我所知,
我什么时候应该使用它们,我为什么要使用它们,我应该如何使用它们?
我对c++相当精通,所以使用更高级的编程概念应该不成问题。如果可能的话,请给我一个相应的c++例子。
虽然
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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') |
解释
让我们假设一个类的例子,处理日期信息(这将是我们的样板文件):
1 2 3 4 5 6 | class Date(object): def __init__(self, day=0, month=0, year=0): self.day = day self.month = month self.year = year |
这个类显然可以用来存储关于特定日期的信息(没有时区信息;让我们假设所有日期都用UTC)表示。
这里我们有一个典型的Python类实例初始化器
类方法
我们有一些任务可以使用
让我们假设我们想要创建许多
所以我们必须做的是:
将字符串解析为三个整数变量或由该变量组成的3项元组,以接收日、月和年。通过将这些值传递给初始化调用来实例化这看起来像:
1 2 | day, month, year = map(int, string_date.split('-')) date1 = Date(day, month, year) |
为此,c++可以通过重载来实现这样的特性,但是Python没有这种重载。相反,我们可以使用
1 2 3 4 5 6 7 | @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') |
让我们更仔细地看看上面的实现,并回顾一下我们在这里有哪些优势:
我们在一个地方实现了日期字符串解析,现在可以重用了。封装在这里工作得很好(如果您认为可以在其他地方将字符串解析实现为单个函数,那么这个解决方案更适合OOP范例)。静态方法
让我们看看下一个用例。
我们有一个想要验证的日期字符串。该任务还在逻辑上绑定到我们目前使用的
这里是
1 2 3 4 5 6 7 | @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') |
因此,从
罗斯季斯拉夫·季科的回答非常恰当。我认为我可以突出说明在创建附加构造函数时应该选择
在上面的例子中,Rostyslav使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
因此,
但是,如果您仔细观察,无论如何,工厂进程都是硬编码来创建
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
在大多数情况下,这是不需要的。如果您想要的是一个知道调用它的类的工厂方法,那么
将
1 2 3 | @classmethod def millenium(cls, month, day): return cls(month, day, 2000) |
确保
1 2 3 4 5 6 7 8 9 | 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" |
原因是,如您现在所知,使用的是
当使用每个时
这里是这个主题的好链接。
Meaning of @classmethod and@staticmethod ?
方法是对象名称空间中的函数,可以作为属性访问。常规方法(即instance)获取实例(我们通常称它为
when should I use them, why should I use them, and how should I use them?
您不需要任何一个装饰器。但是根据您应该最小化函数参数数量的原则(请参阅Clean Coder),这样做很有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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() |
对于实例方法和类方法,不接受至少一个参数就是类型错误,但是不理解该参数的语义就是用户错误。
(定义
1 | some_function_h = some_function_g = some_function_f = lambda x=None: x |
这是可行的。)
点查找实例和类:对实例执行点查找的顺序如下:
类名称空间中的数据描述符(类似于属性)实例注意,实例上的点查找调用如下:
1 2 | instance = Example() instance.regular_instance_method |
方法是可调用的属性:
1 | instance.regular_instance_method() |
的实例方法
参数
您必须从类的实例访问实例方法。
1 2 3 | >>> instance = Example() >>> instance.regular_instance_method() <__main__.Example object at 0x00000000399524E0> |
类方法
参数
您可以通过实例或类(或子类)访问此方法。
1 2 3 4 | >>> instance.a_class_method() <class '__main__.Example'> >>> Example.a_class_method() <class '__main__.Example'> |
静态方法
没有隐式地给出参数。这种方法的工作原理与(例如)在模块名称空间上定义的任何函数一样,只是可以查找它
1 2 | >>> print(instance.a_static_method()) None |
Again, when should I use them, why should I use them?
与实例方法相比,它们传递给方法的信息中的每一个都有越来越多的限制。
当你不需要这些信息的时候就使用它们。
这使您的函数和方法更容易推理和单元测试。
哪个更容易推理?
1 | def function(x, y, z): ... |
或
1 | def function(y, z): ... |
或
1 | def function(z): ... |
参数较少的函数更容易推理。它们也更容易进行单元测试。
这些类似于实例、类和静态方法。记住,当我们有一个实例时,我们也有它的类,再一次,问你自己,哪个更容易推理?
1 2 3 4 5 6 7 8 9 10 11 | 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): ... |
装入的例子
这里有几个我最喜欢的内置例子:
1 2 | >>> 'abc'.translate(str.maketrans({'a': 'b'})) 'bbc' |
1 2 | >>> dict.fromkeys('abc') {'a': None, 'c': None, 'b': None} |
当子类化时,我们看到它以类方法的形式获取类信息,这是非常有用的:
1 2 3 | >>> class MyDict(dict): pass >>> type(MyDict.fromkeys('abc')) <class '__main__.MyDict'> |
我的建议-结论
如果不需要类或实例参数,但是函数与对象的使用相关,并且函数位于对象的名称空间中很方便,那么可以使用静态方法。
当您不需要实例信息,但可能需要类信息用于它的其他类或静态方法时,或者可能需要类信息本身作为构造函数时,请使用类方法。(您不需要硬编码类,这样就可以在这里使用子类。)
当希望根据调用方法的子类更改方法的行为时,可以使用
在使用静态时,您希望在子类之间保持行为不变
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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当您想要编写一个工厂方法并且通过这个自定义属性可以附加到一个类中时,这一点非常重要。可以在继承的类中重写此属性。
这两种方法的比较如下
简而言之,@classmehtod将普通方法转换为工厂方法。
让我们用一个例子来探索一下:
1 2 3 4 5 6 | class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}' |
如果没有@classmethod,您应该一个接一个地创建实例,然后将它们嵌套起来。
1 2 3 4 5 6 | book1 = PythonBook('Learning Python', 'Mark Lutz') In [20]: book1 Out[20]: Book: Learning Python, Author: Mark Lutz book2 = PythonBook('Python Think', 'Allen B Dowey') In [22]: book2 Out[22]: Book: Python Think, Author: Allen B Dowey |
例如@classmethod
1 2 3 4 5 6 7 8 9 10 11 12 | class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}' @classmethod def book1(cls): return cls('Learning Python', 'Mark Lutz') @classmethod def book2(cls): return cls('Python Think', 'Allen B Dowey') |
测试:
1 2 3 4 | In [31]: PythonBook.book1() Out[31]: Book: Learning Python, Author: Mark Lutz In [32]: PythonBook.book2() Out[32]: Book: Python Think, Author: Allen B Dowey |
看到了吗?成功地在类定义中创建实例,并将它们收集在一起。
总之,@classmethod decorator将传统方法转换为工厂方法,使用classmethods可以添加尽可能多的替代构造函数。
我是这个网站的初学者,我已经阅读了上面所有的答案,并且得到了我想要的信息。但是,我没有表决权。所以我想从StackOverflow开始,给出我所理解的答案。
另一种稍微不同的思考方式可能对某些人有用……在超类中使用类方法来定义方法在被不同的子类调用时的行为。当我们想要返回相同的东西时,不管我们调用的子类是什么,都会使用静态方法。
@classmethod
1 2 3 4 5 6 7 8 9 10 | class C: def __init__(self, parameters): .... @classmethod def construct_from_func(cls, parameters): .... obj1 = C(parameters) obj2 = C.construct_from_func(parameters) |
注意,它们都有一个类的引用作为definitioin中的第一个参数,而
@staticmethod
1 2 3 4 5 6 7 8 9 10 11 12 13 | class C: def __init__(self): .... @staticmethod def static_method(args): .... def normal_method(parameters): .... result = C.static_method(parameters) result = obj.normal_method(parameters) |
类方法可以修改类的状态,它绑定到类并包含cls作为参数。
静态方法不能修改类的状态,它绑定到类而不知道类或实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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) |