多个构造函数:Pythonic 方式?
问题描述
我有一个保存数据的容器类.创建容器时,有不同的方法来传递数据.
- 传递一个包含数据的文件
- 直接通过参数传递数据
- 不传递数据;只需创建一个空容器
在 Java 中,我将创建三个构造函数.如果在 Python 中可行的话,它会是这样的:
类容器:def __init__(self):self.timestamp = 0自我数据 = []self.metadata = {}def __init__(自我,文件):f = 文件.open()self.timestamp = f.get_timestamp()self.data = f.get_data()self.metadata = f.get_metadata()def __init__(自我,时间戳,数据,元数据):self.timestamp = 时间戳self.data = 数据self.metadata = 元数据
在 Python 中,我看到了三个明显的解决方案,但没有一个是漂亮的:
A:使用关键字参数:
def __init__(self, **kwargs):如果 kwargs 中的文件":...kwargs 中的 elif 'timestamp' 和 kwargs 中的 'data' 和 kwargs 中的 'metadata':...别的:...创建空容器
B:使用默认参数:
def __init__(self, file=None, timestamp=None, data=None, metadata=None):如果文件:...elif 时间戳以及数据和元数据:...别的:...创建空容器
C:只提供构造函数来创建空容器.提供用不同来源的数据填充容器的方法.
def __init__(self):self.timestamp = 0自我数据 = []self.metadata = {}def add_data_from_file(文件):...def add_data(时间戳,数据,元数据):...
解决方案 A 和 B 基本相同.我不喜欢执行 if/else,尤其是因为我必须检查是否提供了此方法所需的所有参数.如果要通过第四种方法扩展代码以添加数据,A 比 B 更灵活.
解决方案 C 似乎是最好的,但用户必须知道他需要哪种方法.例如:如果他不知道args
是什么,他就不能做c = Container(args)
.
什么是最 Pythonic 的解决方案?
解决方案Python
中不能有多个同名的方法.函数重载 - 与 Java
不同 - 不受支持.
使用默认参数或 **kwargs
和 *args
参数.
您可以使用 @staticmethod
或 @classmethod
装饰器创建静态方法或类方法,以返回您的类的实例,或添加其他构造函数.
我建议你这样做:
F 类:def __init__(self, timestamp=0, data=None, metadata=None):self.timestamp = 时间戳self.data = list() if data is None else dataself.metadata = dict() if metadata is None else metadata@classmethoddef from_file(cls, 路径):_file = cls.get_file(路径)时间戳 = _file.get_timestamp()数据 = _file.get_data()元数据 = _file.get_metadata()返回 cls(时间戳、数据、元数据)@classmethoddef from_metadata(cls,时间戳,数据,元数据):返回 cls(时间戳、数据、元数据)@静态方法def get_file(路径):# ...经过
<块引用>
⚠ 在 python 中,从不将可变类型作为默认值.⚠请参阅此处.
I have a container class that holds data. When the container is created, there are different methods to pass data.
- Pass a file which contains the data
- Pass the data directly via arguments
- Don't pass data; just create an empty container
In Java, I would create three constructors. Here's how it would look like if it were possible in Python:
class Container:
def __init__(self):
self.timestamp = 0
self.data = []
self.metadata = {}
def __init__(self, file):
f = file.open()
self.timestamp = f.get_timestamp()
self.data = f.get_data()
self.metadata = f.get_metadata()
def __init__(self, timestamp, data, metadata):
self.timestamp = timestamp
self.data = data
self.metadata = metadata
In Python, I see three obvious solutions, but none of them is pretty:
A: Using keyword arguments:
def __init__(self, **kwargs):
if 'file' in kwargs:
...
elif 'timestamp' in kwargs and 'data' in kwargs and 'metadata' in kwargs:
...
else:
... create empty container
B: Using default arguments:
def __init__(self, file=None, timestamp=None, data=None, metadata=None):
if file:
...
elif timestamp and data and metadata:
...
else:
... create empty container
C: Only provide constructor to create empty containers. Provide methods to fill containers with data from different sources.
def __init__(self):
self.timestamp = 0
self.data = []
self.metadata = {}
def add_data_from_file(file):
...
def add_data(timestamp, data, metadata):
...
Solutions A and B are basically the same. I don't like doing the if/else, especially since I have to check if all arguments required for this method were provided. A is a bit more flexible than B if the code is ever to be extended by a fourth method to add data.
Solution C seems to be the nicest, but the user has to know which method he requires. For example: he cant do c = Container(args)
if he doesn't know what args
is.
Whats the most Pythonic solution?
解决方案You can't have multiple methods with same name in Python
. Function overloading - unlike in Java
- isn't supported.
Use default parameters or **kwargs
and *args
arguments.
You can make static methods or class methods with the @staticmethod
or @classmethod
decorator to return an instance of your class, or to add other constructors.
I advise you to do:
class F:
def __init__(self, timestamp=0, data=None, metadata=None):
self.timestamp = timestamp
self.data = list() if data is None else data
self.metadata = dict() if metadata is None else metadata
@classmethod
def from_file(cls, path):
_file = cls.get_file(path)
timestamp = _file.get_timestamp()
data = _file.get_data()
metadata = _file.get_metadata()
return cls(timestamp, data, metadata)
@classmethod
def from_metadata(cls, timestamp, data, metadata):
return cls(timestamp, data, metadata)
@staticmethod
def get_file(path):
# ...
pass
⚠ Never have mutable types as defaults in python. ⚠ See here.
相关文章