Python中的Class方法有什么用?

时间:2020-03-05 18:46:04  来源:igfitidea点击:

我正在自学Python,最近的一课是Python不是Java,因此我花了一段时间将所有Class方法转换为函数。

我现在意识到我不需要使用Class方法来完成Java中的static方法,但是现在我不确定何时使用它们。我可以找到的关于Python类方法的所有建议都是像我这样的新手提出的,应该避免使用它们,并且在讨论它们时,标准文档是最不透明的。

有没有人有在Python中使用Class方法的好例子,或者至少有人可以告诉我何时可以明智地使用Class方法?

解决方案

回答

替代构造函数是经典示例。

回答

类方法用于需要不特定于任何特定实例但仍以某种方式涉及到类的方法。关于它们的最有趣的事情是它们可以被子类覆盖,这在Java的静态方法或者Python的模块级函数中根本是不可能的。

如果我们具有类MyClass和在MyClass上运行的模块级函数(工厂,依赖项注入存根等),则将其设为类方法。然后它将可用于子类。

回答

诚实地?我从未找到用于staticmethod或者classmethod的方法。我还没有看到使用全局函数或者实例方法无法完成的操作。

如果python更像Java那样使用私有成员和受保护成员,那将是不同的。在Java中,我需要一个静态方法来访问实例的私有成员以执行操作。在Python中,这几乎没有必要。

通常,当人们真正需要做的就是更好地使用python的模块级命名空间时,我会看到人们使用staticmethod和classmethod。

回答

工厂方法(替代构造函数)确实是类方法的经典示例。

基本上,类方法适合任何我们想拥有自然适合于类的名称空间但不与类的特定实例相关联的方法的时间。

例如,在出色的unipath模块中:

当前目录

由于当前目录是进程范围的,因此cwd方法没有应与之关联的特定实例。但是,将cwd更改为给定Path实例的目录确实是一种实例方法。

嗯...因为Path.cwd()确实返回了Path实例,我想它可以被认为是一种工厂方法...

回答

我曾经使用过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()

聪明吧?

回答

我最近想要一个非常轻量级的日志记录类,该类将输出不同的输出量,具体取决于可以通过编程设置的日志记录级别。但是我不想每次想输出调试消息,错误或者警告时都实例化该类。但是我还想封装该日志记录工具的功能,并在不声明任何全局变量的情况下使其可重用。

因此,我使用了类变量和@ 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()

回答

类方法提供"语义糖"(不知道此术语是否被广泛使用)或者"语义便利"。

示例:我们获得了一组代表对象的类。我们可能想要类方法" all()"或者" find()"来编写" User.all()"或者" User.find(firstname ='Guido')`。当然可以使用模块级功能来完成...

回答

这样想:普通的方法对于隐藏调度的细节很有用:我们可以键入myobj.foo(),而不必担心foo()方法是由myobj对象的类实现还是由其中之一实现的。它的父类。类方法与此完全类似,但是使用类对象:它们使我们可以调用MyClass.foo(),而不必担心MyClass是否专门实现foo(),因为它需要自己专用版本,或者是否让其父类处理该调用。

当我们在创建实际实例之前进行设置或者计算时,类方法是必不可少的,因为在该实例存在之前,我们显然不能将实例用作方法调用的分配点。可以在SQLAlchemy源代码中查看一个很好的示例。在以下链接中查看dbapi()类方法:

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

我们可以看到数据库后端用来按需导入特定于供应商的数据库库的dbapi()方法是一个类方法,因为它需要在开始创建特定数据库连接的实例之前运行但是它不能是简单的函数或者静态函数,因为他们希望它能够调用其他支持的方法,这些方法可能类似地需要在子类中而不是在其父类中更具体地编写。而且,如果我们分派给一个函数或者静态类,那么我们将"忘记"并失去有关哪个类正在执行初始化的知识。