对象的工厂方法-最佳做法? [英] Factory method for objects - best practice?
问题描述
这是一个关于使用python从相同数据的不同形式创建类或类型的实例的最佳实践的问题.使用类方法好还是完全使用单独的函数好?假设我有一个用于描述文档大小的类. (注意:这只是一个例子.我想知道创建类实例的最佳方法,而不是描述文档大小的最佳方法.)
This is a question regarding the best practice for creating an instance of a class or type from different forms of the same data using python. Is it better to use a class method or is it better to use a separate function altogether? Let's say I have a class used to describe the size of a document. (Note: This is simply an example. I want to know the best way to create an instance of the class not the best way to describe the size of a document.)
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
def __init__(self, bits):
self._bits = bits
@property
def bits(self):
return float(self._bits)
@property
def bytes(self):
return self.bits / self.BYTE
@property
def kilobits(self):
return self.bits / self.KILO
@property
def kilobytes(self):
return self.bytes / self.KILO
@property
def megabits(self):
return self.kilobits / self.KILO
@property
def megabytes(self):
return self.kilobytes / self.KILO
我的__init__
方法采用以位表示的大小值(位和只有位,我想保持这种方式),但是可以说我有一个以字节为单位的大小值,并且我想创建我的类的实例.使用类方法是更好还是完全使用单独的函数?
My __init__
method takes a size value represented in bits (bits and only bits and I want to keep it that way) but lets say I have a size value in bytes and I want to create an instance of my class. Is it better to use a class method or is it better to use a separate function altogether?
class Size(object):
"""
Utility object used to describe the size of a document.
"""
BYTE = 8
KILO = 1024
@classmethod
def from_bytes(cls, bytes):
bits = bytes * cls.BYTE
return cls(bits)
OR
def create_instance_from_bytes(bytes):
bits = bytes * Size.BYTE
return Size(bits)
这似乎不是问题,也许两个示例都是有效的,但是每当我需要实现这样的事情时,我都会考虑一下.很长时间以来,我一直偏爱类方法方法,因为我喜欢将类方法和工厂方法联系在一起的组织好处.同样,使用类方法保留了创建任何子类实例的能力,因此它更加面向对象.另一方面,一个朋友曾经说过如果有疑问,请执行标准库的工作",而我还没有在标准库中找到一个示例.
This may not seem like an issue and perhaps both examples are valid but I think about it every time I need to implement something like this. For a long time I have preferred the class method approach because I like the organisational benefits of tying the class and the factory method together. Also, using a class method preserves the ability to create instances of any subclasses so it's more object orientated. On the other hand, a friend once said "When in doubt, do what the standard library does" and I am yet to find an example of this in the standard library.
推荐答案
首先,大多数情况下,您认为不需要这样的东西;这表明您正在尝试将Python像Java一样对待,而解决方案是退后一步,询问为什么需要工厂.
First, most of the time you think you need something like this, you don't; it's a sign that you're trying to treat Python like Java, and the solution is to step back and ask why you need a factory.
通常,最简单的方法是仅使用带有默认/可选/关键字参数的构造函数.即使在Java中您从未用这种方式编写的情况(甚至在C ++或ObjC中重载的构造函数都会感到错误的情况)在Python中也看起来很自然.例如,size = Size(bytes=20)
或size = Size(20, Size.BYTES)
看起来很合理.因此,从Size
继承并完全不添加任何内容的Bytes(20)
类,除了__init__
重载看起来是合理的.这些定义起来很简单:
Often, the simplest thing to do is to just have a constructor with defaulted/optional/keyword arguments. Even cases that you'd never write that way in Java—even cases where overloaded constructors would feel wrong in C++ or ObjC—may look perfectly natural in Python. For example, size = Size(bytes=20)
, or size = Size(20, Size.BYTES)
look reasonable. For that matter, a Bytes(20)
class that inherits from Size
and adds absolutely nothing but an __init__
overload looks reasonable. And these are trivial to define:
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):
或者:
BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
但是,有时候您要做需要工厂功能.那么,您该怎么办?好吧,通常有两种东西会被集中到工厂"中.
But, sometimes you do need factory functions. So, what do you do then? Well, there are two kinds of things that are often lumped together into "factories".
@classmethod
是执行替代构造函数"的惯用方式-stdlib上有示例-itertools.chain.from_iterable
,datetime.datetime.fromordinal
等.
A @classmethod
is the idiomatic way to do an "alternate constructor"—there are examples all over the stdlib—itertools.chain.from_iterable
, datetime.datetime.fromordinal
, etc.
函数是执行我不在乎实际的类是"工厂的惯用方式.查看例如内置的open
函数.您知道它在3.3中返回什么吗?你关心?没有.这就是为什么它是一个函数,而不是io.TextIOWrapper.open
之类的原因.
A function is the idiomatic way to do an "I don't care what the actual class is" factory. Look at, e.g., the built-in open
function. Do you know what it returns in 3.3? Do you care? Nope. That's why it's a function, not io.TextIOWrapper.open
or whatever.
您给出的示例似乎是一个完全合法的用例,非常清楚地适合替代构造函数" bin(如果它不适合带有额外参数的构造函数" bin).
Your given example seems like a perfectly legitimate use case, and fits pretty clearly into the "alternate constructor" bin (if it doesn't fit into the "constructor with extra arguments" bin).
这篇关于对象的工厂方法-最佳做法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!