我正在自学Python,我最近的一课是Python不是Java,所以我刚刚花了一段时间把我所有的Class方法变成了函数。
我现在意识到,我不需要使用Class方法来做我在Java中使用静态方法所做的事情,但现在我不确定什么时候我会使用它们。我能找到的所有关于Python类方法的建议都是,像我这样的新手应该避开它们,而标准文档在讨论它们时是最不透明的。
谁有一个在Python中使用类方法的好例子,或者至少有人能告诉我什么时候可以合理地使用类方法吗?
我正在自学Python,我最近的一课是Python不是Java,所以我刚刚花了一段时间把我所有的Class方法变成了函数。
我现在意识到,我不需要使用Class方法来做我在Java中使用静态方法所做的事情,但现在我不确定什么时候我会使用它们。我能找到的所有关于Python类方法的建议都是,像我这样的新手应该避开它们,而标准文档在讨论它们时是最不透明的。
谁有一个在Python中使用类方法的好例子,或者至少有人能告诉我什么时候可以合理地使用类方法吗?
当前回答
我最近想要一个非常轻量级的日志类,它可以根据可编程设置的日志级别输出不同数量的输出。但我不想每次输出调试消息、错误或警告时都实例化这个类。但是我还想封装这个日志记录工具的功能,并使其在不声明任何全局变量的情况下可重用。
所以我使用类变量和@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()
其他回答
@classmethod对于从外部资源轻松实例化该类的对象非常有用。考虑以下几点:
import settings
class SomeClass:
@classmethod
def from_settings(cls):
return cls(settings=settings)
def __init__(self, settings=None):
if settings is not None:
self.x = settings['x']
self.y = settings['y']
然后在另一个文件中:
from some_package import SomeClass
inst = SomeClass.from_settings()
访问inst.x将得到与settings['x']相同的值。
我最近想要一个非常轻量级的日志类,它可以根据可编程设置的日志级别输出不同数量的输出。但我不想每次输出调试消息、错误或警告时都实例化这个类。但是我还想封装这个日志记录工具的功能,并使其在不声明任何全局变量的情况下可重用。
所以我使用类变量和@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()
如果你不是一个“训练有素的程序员”,这应该会有帮助:
我想我已经理解了上面和网上其他地方的技术解释,但我总是有一个问题:“不错,但我为什么需要它?”什么是实际的用例?”现在生活给了我一个很好的例子来阐明一切:
我使用它来控制由多线程模块实例化的类的实例之间共享的全局共享变量。在人性化的语言中,我正在运行多个代理,为深度学习并行创建示例。(想象多个玩家同时玩ATARI游戏,每个人都将他们的游戏结果保存到一个公共存储库(SHARED VARIABLE))
我用以下代码实例化玩家/代理(在主/执行代码中):
a3c_workers = [A3C_Worker(self.master_model, self.optimizer, i, self.env_name, self.model_dir) for i in range(multiprocessing.cpu_count())]
它创造了和我的电脑上有多少处理器核心一样多的玩家 A3C_Worker——定义代理的类 A3c_workers -是该类实例的列表(即每个实例都是一个玩家/代理)
现在我想知道所有玩家/代理玩了多少游戏,因此在A3C_Worker定义中,我定义了所有实例共享的变量:
class A3C_Worker(threading.Thread):
global_shared_total_episodes_across_all_workers = 0
现在,当工人们完成他们的游戏时,每完成一场比赛,他们都会增加1个数字
在我的示例生成结束时,我关闭了实例,但共享变量已经分配了所玩游戏的总数。所以当我重新运行它时,我最初的总集数是之前的总和。但是我需要这个计数来代表每次单独运行的值
为了解决这个问题,我指定:
class A3C_Worker(threading.Thread):
@classmethod
def reset(cls):
A3C_Worker.global_shared_total_episodes_across_all_workers = 0
在执行代码中调用:
A3C_Worker.reset()
注意,它是对CLASS整体的调用,而不是它单独的任何实例。因此,从现在开始,对于我发起的每个新代理,它将把我的计数器设置为0。
使用通常的方法定义def play(self):,将需要我们为每个实例单独重置计数器,这将需要更多的计算,并且难以跟踪。
我认为最明确的答案是AmanKow的答案。归根结底,这取决于你想如何组织你的代码。你可以把所有东西都写成模块级的函数,这些函数被包装在模块的命名空间中
module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass
usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass
def f5():pass
usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()
上面的过程代码组织得不好,正如你所看到的,只有3个模块后,它变得令人困惑,每个方法是做什么的?你可以为函数使用较长的描述性名称(如在java中),但你的代码仍然很快变得难以管理。
面向对象的方法是将代码分解为可管理的块,即类和对象,函数可以与对象、实例或类相关联。
与模块级函数相比,使用类函数可以在代码中获得另一个级别的除法。 因此,您可以在类中对相关函数进行分组,使它们更特定于分配给该类的任务。例如,你可以创建一个文件工具类:
class FileUtil ():
def copy(source,dest):pass
def move(source,dest):pass
def copyDir(source,dest):pass
def moveDir(source,dest):pass
//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")
这种方式更灵活,更可维护,您将函数分组在一起,并且每个函数的功能更明显。此外,您还可以防止名称冲突,例如,函数副本可能存在于您在代码中使用的另一个导入模块中(例如网络副本),因此当您使用全名FileUtil.copy()时,您可以消除这个问题,并且两个复制函数可以并排使用。
我以前用过PHP,最近我在问自己,这个类方法是怎么回事?Python手册是非常技术性的,非常简短的文字,所以它不会帮助理解这个功能。我一直在谷歌上搜索,然后我找到了答案——> http://code.anjanesh.net/2007/12/python-classmethods.html。
如果你懒得点击它。我的解释很简短。:)
在PHP中(也许不是所有人都知道PHP,但是这个语言非常直接,每个人都应该明白我在说什么),我们有这样的静态变量:
class A
{
static protected $inner_var = null;
static public function echoInnerVar()
{
echo self::$inner_var."\n";
}
static public function setInnerVar($v)
{
self::$inner_var = $v;
}
}
class B extends A
{
}
A::setInnerVar(10);
B::setInnerVar(20);
A::echoInnerVar();
B::echoInnerVar();
在这两种情况下,输出都是20。
但是在python中,我们可以添加@classmethod装饰器,这样就可以分别输出10和20。例子:
class A(object):
inner_var = 0
@classmethod
def setInnerVar(cls, value):
cls.inner_var = value
@classmethod
def echoInnerVar(cls):
print cls.inner_var
class B(A):
pass
A.setInnerVar(10)
B.setInnerVar(20)
A.echoInnerVar()
B.echoInnerVar()
聪明,不是吗?