何时以及如何被假设静态方法在Python中使用? 我们使用类方法,如工厂方法来创建对象的实例已经建立,应尽量避免。 换句话说,它是不使用类方法作为替代构造(参见最佳实践厂方法Python对象-最佳实践 )。
可以说我有用于表示数据库中的一些实体数据的类。 想象中的数据是一个dict
包含字段名称和字段值和字段之一是一个ID号,使得该数据唯一对象。
class Entity(object):
def __init__(self, data, db_connection):
self._data = data
self._db_connection
在这里我__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)
以上是不要做什么,或者至少不要做什么,如果有一个替代的例子。 如果编辑我的类方法,以便它更是一个实用的方法和更小的工厂方法现在我想知道是一个有效的解决方案。 换句话说,做了下面的例子符合使用静态方法的最佳实践。
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)
或者它更有意义,使用一个独立的函数从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)
注:我不想改变我的__init__
方法。 此前人们曾建议让我__init__
方法看起来像这样__init__(self, data=None, id_number=None)
,但有可能是101种不同的方式来找到实体数据,所以我宁愿保持这种逻辑中分离到一定程度。 合理?
何时以及如何被假设静态方法在Python中使用?
圆滑的解释是:不是很频繁。
偶数glibber但不完全无用的答案是:当他们使你的代码更易读。
首先,让我们先绕道的文档 :
在Python静态方法类似于Java或C ++中。 也见classmethod()
为一个变体,其是用于创建备用类构造是有用的。
所以,当你需要在C ++中的静态方法,你需要在Python静态方法,对吗?
哦,不。
在Java中,没有任何功能,只是方法,所以你最终建立的伪类,只是捆绑的静态方法。 做同样的事情在Python的方法是只使用免费的功能。
这是很明显的。 然而,这是很好的Java风格一样努力地寻找合适的类楔形的功能进去,这样就可以避免写那些伪类,而做同样的事情是不好的Python风格再次,使用免费的功能,而这很明显要少得多。
C ++不具有相同的限制,如Java,但许多C ++的风格很相似呢。 (在另一方面,如果你是一个“现代C ++”程序员谁是内化的“自由函数是一类接口的一部分”的成语,你的直觉为“哪里是有用的静态方法”可能是Python的相当不错。)
但是,如果你在这个从第一原理的到来,而不是从另一种语言,有一个更简单的看待事物的方式:
一个@staticmethod
基本上是一个全球性的功能。 如果你有一个函数foo_module.bar()
这将是更具可读性出于某种原因,如果它被拼写为foo_module.BazClass.bar()
使之成为@staticmethod
。 如果没有,没有。 这真的就是这么简单。 唯一的问题是建立自己的直觉什么是更具可读性的地道的Python程序员。
当然,使用@classmethod
当你需要访问类,但没有实例替代构造函数的范例情况下,作为文档暗示。 虽然你经常可以模拟@classmethod
与@staticmethod
只是通过明确引用类(尤其是当你没有太多的子类),你不应该。
最后,让你的具体问题:
如果唯一的理由客户不断需要通过ID查找数据是构建一个Entity
,这听起来像是你不应该暴露的实现细节,这也使得客户端代码更加复杂。 只需使用一个构造函数。 如果你不希望修改__init__
(和你说得对,还有你可能不希望很好的理由),使用@classmethod
作为一个可选的构造: Entity.from_id(id_number, db_connection)
在另一方面,如果查找的东西是在什么都没有做其他情况下客户本身有用的Entity
建筑,好像这有什么用做Entity
类(或至少不比什么都重要同一模块)。 所以,仅仅使它成为一个自由的功能。
答案的链接的问题明确这样说:
甲@classmethod是做一个“交替构造函数” -there是示例遍布STDLIB-itertools.chain.from_iterable,datetime.datetime.fromordinal等惯用方式
所以我不知道你是怎么上心,使用一个类方法本质上是不好的。 其实,我喜欢你的具体情况使用类方法的想法,因为它使下面的代码,并使用API容易。
另一种方法是使用默认的构造函数的参数如下所示:
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)
我喜欢你写不过原来的类方法的版本。 特别是因为data
是相当不明确的。
你的第一个例子是最有意义的对我说: Entity.from_id
是相当简洁明了。
它避免了使用的data
在接下来的两个例子,这并不说明就是BEING返回了什么; 该data
被用于构建Entity
。 如果您想具体了解的data
被用来构造的Entity
,那么你能说出你的方法类似Entity.with_data_for_id
或同等功能entity_with_data_for_id
。
使用动词如find
也可以是相当混乱,因为它不会给返回值的任何迹象-是函数应该当它找到的数据做什么? (是的,我知道str
有一个find
方法,那岂不是更好地命名index_of
但后来有也? index
...)这让我想起了经典的:
我总是尽量想什么名字将指示有人用(a)任何知识体系,以及(b)该系统的其他部分的知识 - 不是说我总能成功!
这里是一个体面的使用情况@staticmethod。
我一直对游戏作为辅助项目。 那场比赛的第一部分包括基于统计掷骰子,并拿起物品和效果影响你的性格的统计(或好或坏)的可能性。
当我摇在我的游戏骰子,我需要基本上说...以基字符统计,然后添加任何库存和效果统计到这个盛大的网状图。
你不能把这些抽象的对象,并将它们添加,而不指示程序如何。 我不这样做在类级别或实例级别的东西要么。 我不想在一些全局模块定义的功能。 最后一个最好的选择是用一个静态方法去增加了统计信息集中在一起。 它只是最有意义这种方式。
class Stats:
attribs = ['strength', 'speed', 'intellect', 'tenacity']
def __init__(self,
strength=0,
speed=0,
intellect=0,
tenacity=0
):
self.strength = int(strength)
self.speed = int(speed)
self.intellect = int(intellect)
self.tenacity = int(tenacity)
# combine adds stats objects together and returns a single stats object
@staticmethod
def combine(*args: 'Stats'):
assert all(isinstance(arg, Stats) for arg in args)
return_stats = Stats()
for stat in Stats.attribs:
for _ in args:
setattr(return_stats, stat,
getattr(return_stats, stat) + getattr(_, stat))
return (return_stats)
这将使统计组合调用像这样工作
a = Stats(strength=3, intellect=3)
b = Stats(strength=1, intellect=-1)
c = Stats(tenacity=5)
print(Stats.combine(a, b, c).__dict__)
{ '强度':4, '速度':0, '智力':2 '韧性':5}