• Fluent Python: Classmethod vs Staticmethod


    Fluent Python一书9.4节比较了 Classmethod 和 Staticmethod 两个装饰器的区别:

    给出的结论是一个非常有用(Classmethod), 一个不太有用(Staticmethod).

    今天我们就对这两个装饰器做更深入的了解和比较,

    (一) Classmethod:

    (1)什么时候使用Classmethod?

    classmethod最常见的用途是定义备选构造方法

    (2)如何使用Classmethod?

    下面我们用一个示例来展示如何使用classmethod,

    假如我们设计了一个日期类:

    class Date:
        def __init__(self, day=0, month=0, year=0):
            self.day = day
            self.month = month
            self.year = year

    显然这个类是可以用来存储日期的(不包含时区)

    现在我们需要从格式"dd-mm-yyyy"的字符串创建很多Date类的实例,我们可能会在类外部先处理字符串,然后创建Date的实例:

    string_date = "27-09-2017"
    day, month, year = map(int, string_date.split('-'))
    date1 = Date(day, month, year)

    以上的方式可行,然而一个更为Pythonic的方式是使用classmethod:

    class Date:
        def __init__(self, day=0, month=0, year=0):
            self.day = day
            self.month = month
            self.year = year
    
        @classmethod
        def from_string(cls, string_date):
            day, month, year = map(int, string_date.split('-'))
            return cls(day, month, year)

    在上面的Code里,from_string() 方法可以看作除了__init__之外的另一个构造方法,这个构造方法可以从日期格式字符直接创建实例:

    string_date = "27-09-2017"
    date2 = Date.from_string(string_date)

    对于以上的Date类实现,我们需要看到classmethod装饰器的三个优点:

    (a) 日期格式字符串处理是在一个地方,是可重用的

    (b)比起在Date类外实现日期格式字符串处理,这里的封装更符合面向对象思想

    (c)对于Date类可能有的子类,他们自动继承了from_string()方法

    (二) Staticmethod:

    (1)什么时候使用Staticmethod?

    我从未见过不得不用staticmethod的情况, 如果想定义不需要与类交互的函数,那么在模块中定义也是完全可以的

    (2)如何使用Staticmethod?

    假如我们设计了一个Server类,有地址和端口两个属性:

    class Server:
        def __init__(self, ip_address, port):
            self.ip_address = ip_address
            self.port = port

    现在我需要一个函数检查用户输入的是否是一个ipv4地址,这就可以用staticmethod来实现,因为检查ipv4地址的合法性和类本身并不需要交互:

    import re
    
    
    class Server:
        def __init__(self, ip_address, port):
            self.ip_address = ip_address
            self.port = port
    
        @staticmethod
        def is_ipv4(ip_string):
            p = re.compile('^((25[0-5]|2[0-4]d|[01]?dd?).){3}(25[0-5]|2[0-4]d|[01]?dd?)$')
            if p.match(ip_string):
                return True
            else:
                return False

    那么在该Server类中,只要涉及到检查ipv4的地方,我们都可以用到这个is_ipv4()方法了

    其实这个方法也完全可以定义在模块里,效果是一样的

    最后我们回到Fluent Python一书中对classmethod和staticmetho在行为上做的对比:

    书中定义了Demo类,实现了一个classmethod,一个staticmethod:

    class Demo:
        @classmethod
        def classmethod_demo(*args):
            return args
    
        @staticmethod
        def staticmethod_demo(*args):
            return args

    然后我们在控制台观察输出:

    >>> import Example9_4
    <Example9_4.Demo object at 0x7fd0b6dd6cf8>
    >>> Example9_4.Demo.classmethod_demo()
    (<class 'Example9_4.Demo'>,)
    >>> Example9_4.Demo.staticmethod_demo()
    ()
    >>> Example9_4.Demo.classmethod_demo('foo')
    (<class 'Example9_4.Demo'>, 'foo')
    >>> Example9_4.Demo.staticmethod_demo('foo')
    ('foo',)

    我们可以看到无论怎么调用classmethod,第一个参数总是类名Demo,而staticmethod的行为与普通的函数类似。

  • 相关阅读:
    SVG.js 文本绘制整理
    SVG.js 基础图形绘制整理(二)
    SVG.js 基础图形绘制整理(一)
    C# 异步编程Task整理(一)
    Svg.Js 父类的基础操作
    Svg.Js A标签,链接操作
    Svg.Js 简介(转)
    SVG 相关整理
    Kendo UI
    Kendo UI
  • 原文地址:https://www.cnblogs.com/z-joshua/p/7601578.html
Copyright © 2020-2023  润新知