我正在自学Python,我最近的一课是Python不是Java,所以我刚刚花了一段时间把我所有的Class方法变成了函数。
我现在意识到,我不需要使用Class方法来做我在Java中使用静态方法所做的事情,但现在我不确定什么时候我会使用它们。我能找到的所有关于Python类方法的建议都是,像我这样的新手应该避开它们,而标准文档在讨论它们时是最不透明的。
谁有一个在Python中使用类方法的好例子,或者至少有人能告诉我什么时候可以合理地使用类方法吗?
我正在自学Python,我最近的一课是Python不是Java,所以我刚刚花了一段时间把我所有的Class方法变成了函数。
我现在意识到,我不需要使用Class方法来做我在Java中使用静态方法所做的事情,但现在我不确定什么时候我会使用它们。我能找到的所有关于Python类方法的建议都是,像我这样的新手应该避开它们,而标准文档在讨论它们时是最不透明的。
谁有一个在Python中使用类方法的好例子,或者至少有人能告诉我什么时候可以合理地使用类方法吗?
当前回答
这是一个有趣的话题。我对它的理解是,python的classmethod操作起来像一个单例而不是一个工厂(它返回一个类的生成实例)。它是单例的原因是存在一个公共对象(字典),但只为类生成一次,但由所有实例共享。
为了说明这一点,这里有一个例子。注意,所有实例都有一个对单个字典的引用。这不是我理解的工厂模式。这可能是python独有的。
class M():
@classmethod
def m(cls, arg):
print "arg was", getattr(cls, "arg" , None),
cls.arg = arg
print "arg is" , cls.arg
M.m(1) # prints arg was None arg is 1
M.m(2) # prints arg was 1 arg is 2
m1 = M()
m2 = M()
m1.m(3) # prints arg was 2 arg is 3
m2.m(4) # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
M.m(5) # prints arg was 4 arg is 5
其他回答
当然,类定义了一组实例。类的方法作用于单个实例。类方法(和变量)用于将与实例集相关的其他信息挂起。
例如,如果你的类定义了一组学生,你可能想要类变量或方法来定义学生可以成为成员的年级集。
您还可以使用类方法定义用于处理整个集合的工具。例如,Student.all_of_em()可能返回所有已知的学生。显然,如果您的实例集具有比集合更多的结构,您可以提供类方法来了解该结构。Students.all_of_em(等级=“下属”)
这样的技术往往会导致将实例集的成员存储到植根于类变量的数据结构中。这时,您需要注意避免使垃圾收集受挫。
我也问过自己几次同样的问题。尽管这里的人试图努力解释它,恕我直言,我找到的最好的答案(也是最简单的)答案是Python文档中对Class方法的描述。
还有对静态方法的引用。如果有人已经知道实例方法(我假设是这样),这个答案可能是把它们放在一起的最后一块……
关于这个主题的进一步和更深入的阐述也可以在文档中找到: 标准类型层次结构(向下滚动到实例方法部分)
这是一个有趣的话题。我对它的理解是,python的classmethod操作起来像一个单例而不是一个工厂(它返回一个类的生成实例)。它是单例的原因是存在一个公共对象(字典),但只为类生成一次,但由所有实例共享。
为了说明这一点,这里有一个例子。注意,所有实例都有一个对单个字典的引用。这不是我理解的工厂模式。这可能是python独有的。
class M():
@classmethod
def m(cls, arg):
print "arg was", getattr(cls, "arg" , None),
cls.arg = arg
print "arg is" , cls.arg
M.m(1) # prints arg was None arg is 1
M.m(2) # prints arg was 1 arg is 2
m1 = M()
m2 = M()
m1.m(3) # prints arg was 2 arg is 3
m2.m(4) # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
M.m(5) # prints arg was 4 arg is 5
我最近想要一个非常轻量级的日志类,它可以根据可编程设置的日志级别输出不同数量的输出。但我不想每次输出调试消息、错误或警告时都实例化这个类。但是我还想封装这个日志记录工具的功能,并使其在不声明任何全局变量的情况下可重用。
所以我使用类变量和@classmethod装饰器来实现这一点。
使用简单的Logging类,我可以做到以下几点:
Logger._level = Logger.DEBUG
然后,在我的代码中,如果我想输出一堆调试信息,我就必须编写代码
Logger.debug( "this is some annoying message I only want to see while debugging" )
错误是可以改正的
Logger.error( "Wow, something really awful happened." )
在“生产”环境中,我可以指定
Logger._level = Logger.ERROR
现在,将只输出错误消息。调试消息将不会被打印。
这是我的班级:
class Logger :
''' Handles logging of debugging and error messages. '''
DEBUG = 5
INFO = 4
WARN = 3
ERROR = 2
FATAL = 1
_level = DEBUG
def __init__( self ) :
Logger._level = Logger.DEBUG
@classmethod
def isLevel( cls, level ) :
return cls._level >= level
@classmethod
def debug( cls, message ) :
if cls.isLevel( Logger.DEBUG ) :
print "DEBUG: " + message
@classmethod
def info( cls, message ) :
if cls.isLevel( Logger.INFO ) :
print "INFO : " + message
@classmethod
def warn( cls, message ) :
if cls.isLevel( Logger.WARN ) :
print "WARN : " + message
@classmethod
def error( cls, message ) :
if cls.isLevel( Logger.ERROR ) :
print "ERROR: " + message
@classmethod
def fatal( cls, message ) :
if cls.isLevel( Logger.FATAL ) :
print "FATAL: " + message
还有一些代码可以稍微测试一下:
def logAll() :
Logger.debug( "This is a Debug message." )
Logger.info ( "This is a Info message." )
Logger.warn ( "This is a Warn message." )
Logger.error( "This is a Error message." )
Logger.fatal( "This is a Fatal message." )
if __name__ == '__main__' :
print "Should see all DEBUG and higher"
Logger._level = Logger.DEBUG
logAll()
print "Should see all ERROR and higher"
Logger._level = Logger.ERROR
logAll()
类方法用于当您需要不特定于任何特定实例,但仍以某种方式涉及类的方法时。最有趣的是,它们可以被子类覆盖,这在Java的静态方法或Python的模块级函数中是不可能的。
如果你有一个类MyClass,和一个模块级的函数,它操作MyClass(工厂,依赖注入存根等),让它成为一个类方法。然后它将可用于子类。