在python中使用静态方法 - 最佳实践

Yan*_*ani 13 python static-methods coding-style

何时以及如何在python中使用静态方法?我们已经建立了使用类方法作为工厂方法来创建对象的实例应该尽可能避免.换句话说,使用类方法作为替代构造函数并不是最佳实践(请参阅python对象的Factory方法 - 最佳实践).

假设我有一个用于表示数据库中某些实体数据的类.想象一下,数据是一个dict包含字段名称和字段值的对象,其中一个字段是使数据唯一的ID号.

class Entity(object):
    def __init__(self, data, db_connection):
        self._data = data
        self._db_connection
Run Code Online (Sandbox Code Playgroud)

这里我的__init__方法采用实体数据dict对象.假设我只有一个ID号,我想创建一个Entity实例.首先,我需要找到其余的数据,然后创建我的Entity对象的实例.从我之前的问题中,我们确定应尽可能避免使用类方法作为工厂方法.

class Entity(object):

    @classmethod
    def from_id(cls, id_number, db_connection):
        filters = [['id', 'is', id_number]]
        data = db_connection.find(filters)
        return cls(data, db_connection)

    def __init__(self, data, db_connection):
        self._data = data
        self._db_connection


# Create entity
entity = Entity.from_id(id_number, db_connection)
Run Code Online (Sandbox Code Playgroud)

上面是一个例子,说明如果有替代方案,不做或至少不做什么.现在我想知道是否编辑我的类方法,以便它更像是一个实用工具方法,而更少的工厂方法是一个有效的解决方案.换句话说,以下示例是否符合使用静态方法的最佳实践.

class Entity(object):

    @staticmethod
    def data_from_id(id_number, db_connection):
        filters = [['id', 'is', id_number]]
        data = db_connection.find(filters)
        return data


# Create entity
data = Entity.data_from_id(id_number, db_connection)
entity = Entity(data)
Run Code Online (Sandbox Code Playgroud)

或者使用独立函数从ID号中查找实体数据更有意义.

def find_data_from_id(id_number, db_connection):
    filters = [['id', 'is', id_number]]
    data = db_connection.find(filters)
    return data


# Create entity.
data = find_data_from_id(id_number, db_connection)
entity = Entity(data, db_connection)
Run Code Online (Sandbox Code Playgroud)

注意:我不想改变我的__init__方法.以前人们建议让我的__init__方法看起来像这样,__init__(self, data=None, id_number=None)但可能有101种不同的方法来查找实体数据,所以我宁愿在某种程度上保持逻辑分离.合理?

aba*_*ert 24

何时以及如何在python中使用静态方法?

滑稽的回答是:不常见.

平滑而不是无用的答案是:当它们使你的代码更具可读性时.


首先,让我们绕道一下文档:

Python中的静态方法与Java或C++中的静态方法类似.另请参阅classmethod()对于创建备用类构造函数有用的变体.

所以,当你需要一个C++中的静态方法时,你需要一个Python中的静态方法,对吧?

好吧,不.

在Java中,没有函数,只有方法,因此您最终创建的伪类只是静态方法的捆绑.在Python中执行相同操作的方法是使用自由函数.

这很明显.但是,对于一个合适的类来楔入一个函数来说,尽可能地寻找一个好的Java风格,所以你可以避免编写那些伪类,同时做同样的事情就是糟糕的Python风格 - 再次使用自由函数 - 这个不太明显.

C++与Java没有相同的限制,但是许多C++样式无论如何都非常相似.(另一方面,如果你是一个"现代C++"程序员,内化了"自由函数是类接口的一部分"这个习惯用法,你对"静态方法有用"的直觉对于Python来说可能相当不错.)


但是,如果你是从第一原则而不是从另一种语言来实现这一目标,那么有一种更简单的方法来看待事物:

A @staticmethod基本上只是一个全局函数.如果你有一个功能foo_module.bar(),这将是更具可读性出于某种原因,如果它被拼写为foo_module.BazClass.bar(),使它成为一个@staticmethod.如果没有,不要.这就是它的全部内容.唯一的问题是为惯用的Python程序员提供更具可读性的本能.

当然,@classmethod当你需要访问类时,可以使用a ,但实例 - 备用构造函数不是范例,正如文档所暗示的那样.虽然你经常可以模拟@classmethod一个@staticmethod通过明确引用类(尤其是当你没有太多的子类),只是,你不应该.


最后,了解您的具体问题:

如果客户需要通过ID查找数据的唯一原因是构建一个Entity,这听起来像一个你不应该暴露的实现细节,它也使客户端代码更复杂.只需使用构造函数.如果您不想修改自己__init__(并且您认为有充分理由可能不想这样做),请使用a @classmethod作为替代构造函数:Entity.from_id(id_number, db_connection).

另一方面,如果该查找对于与Entity构造无关的其他情况下客户端本身有用的东西,似乎这与Entity该类无关(或者至少与同一模块中的任何其他内容无关)).所以,让它成为一个自由的功能.


Jos*_*ton 11

相关问题的答案具体说明了这一点:

@classmethod是执行"替代构造函数"的惯用方法 - 遍及stdlib-itertools.chain.from_iterable,datetime.datetime.fromordinal等的示例.

所以我不知道你是怎么认为使用类方法本质上是坏的.我实际上喜欢在特定情况下使用classmethod的想法,因为它遵循代码并使用api easy.

替代方法是使用默认构造函数参数,如下所示:

class Entity(object):
    def __init__(self, id, db_connection, data=None):
        self.id = id
        self.db_connection = db_connection
        if data is None:
            self.data = self.from_id(id, db_connection)
        else:
            self.data = data

    def from_id(cls, id_number, db_connection):
        filters = [['id', 'is', id_number]]
        return db_connection.find(filters)
Run Code Online (Sandbox Code Playgroud)

我更喜欢你最初编写的classmethod版本.特别是因为data相当模糊.


Nic*_*ley 7

你的第一个例子对我来说最有意义:Entity.from_id非常简洁明了.

它避免data在接下来的两个例子中使用,这两个例子没有描述返回的内容; 在data用于构建的Entity.如果你想具体了解data用于构造的东西Entity,那么你可以将你的方法命名Entity.with_data_for_id为类似函数或等效函数entity_with_data_for_id.

使用像这样的动词find也可能很混乱,因为它没有给出返回值的任何指示 - 当它找到数据时应该做什么功能?(是的,我意识到str有一种find方法;它不会更好地命名index_of吗?但是那时还有index......)它让我想起经典:

找到x

我总是试着想一个名字会给某人带来什么(a)不了解系统,以及(b)对系统其他部分的了解 - 并不是说​​我总是成功!

  • 关于“查找”的好点。这就像旧的 Scott Adams 文本冒险,因为主要的难题是弄清楚要给通用的“使用”动词赋予哪些名词。(在 Infocom 游戏中,你会“从线轴上取下绳子,把它系在钩子上,然后滑下绳子”;在 SA 游戏中,你会“使用线轴。使用钩子。使用绳索。” ) (2认同)