假设我有一个类,它有一个名为 data 的成员,它是一个列表。
我希望能够使用例如文件名(其中包含用于初始化列表的数据)或使用实际列表来初始化类。
你这样做的技术是什么?
您只是通过查看 __class__
来检查类型吗?
我可能会错过一些技巧吗?
我习惯了 C++ 中的参数类型重载很容易。
获得“备用构造函数”的一种更简洁的方法是使用类方法。例如:
>>> class MyData:
... def __init__(self, data):
... "Initialize MyData from a sequence"
... self.data = data
...
... @classmethod
... def fromfilename(cls, filename):
... "Initialize MyData from a file"
... data = open(filename).readlines()
... return cls(data)
...
... @classmethod
... def fromdict(cls, datadict):
... "Initialize MyData from a dict's items"
... return cls(datadict.items())
...
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]
它更简洁的原因是毫无疑问预期的类型是什么,并且您不必猜测调用者打算如何处理它给您的数据类型。 isinstance(x, basestring)
的问题是调用者无法告诉您,例如,即使类型不是基本字符串,您也应该将其视为字符串(而不是另一个序列)。也许调用者想将同一类型用于不同目的,有时作为单个项目,有时作为项目序列。显式可以消除所有疑问,并导致更健壮和更清晰的代码。
很好的问题。我也解决了这个问题,虽然我同意“工厂”(类方法构造函数)是一个好方法,但我想建议另一种方法,我也发现它非常有用:
这是一个示例(这是一个 read
方法而不是构造函数,但想法是相同的):
def read(self, str=None, filename=None, addr=0):
""" Read binary data and return a store object. The data
store is also saved in the interal 'data' attribute.
The data can either be taken from a string (str
argument) or a file (provide a filename, which will
be read in binary mode). If both are provided, the str
will be used. If neither is provided, an ArgumentError
is raised.
"""
if str is None:
if filename is None:
raise ArgumentError('Please supply a string or a filename')
file = open(filename, 'rb')
str = file.read()
file.close()
...
... # rest of code
这里的关键思想是使用 Python 对命名参数的出色支持来实现这一点。现在,如果我想从文件中读取数据,我会说:
obj.read(filename="blob.txt")
为了从字符串中读取它,我说:
obj.read(str="\x34\x55")
这样,用户只需调用一个方法。如您所见,在内部处理它并不太复杂
obj.read(str="\x34\x55")
是如何处理的;当 str 不是 None
时,您没有可以处理的代码
使用 python3,您可以使用 Implementing Multiple Dispatch with Function Annotations,因为 Python Cookbook 写道:
import time
class Date(metaclass=MultipleMeta):
def __init__(self, year:int, month:int, day:int):
self.year = year
self.month = month
self.day = day
def __init__(self):
t = time.localtime()
self.__init__(t.tm_year, t.tm_mon, t.tm_mday)
它的工作原理如下:
>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
__init__
函数的想法很有趣,你能解释一下这背后的原理吗?
MultipleMeta
中的@GoingMyWay __prepare__
方法返回一个 MultiDict
实例来替换 __new__
方法中 clsdict
传递的 Date
类默认__dict__
属性。因此,它可以容纳多个同名'__init__
'的函数,其值为一个MultiMethod
实例,在它的_method属性中存储了不同的函数注释。您应该检查 Python Cookbook 以了解更多详细信息。
3.6.8
尝试过,它抱怨 TypeError: __init__() takes 2 positional arguments but 3 were given
。在我的代码中,它是 init(self, x)
和 init(self, a,b)
,后者将从前者调用。
快速而肮脏的修复
class MyData:
def __init__(string=None,list=None):
if string is not None:
#do stuff
elif list is not None:
#do other stuff
else:
#make data empty
然后你可以调用它
MyData(astring)
MyData(None, alist)
MyData()
MyData(list = alist)
。
__init__
中的self
吗?您可能不想使用 list
作为输入名称,因为它会隐藏内置类型 list
。
更好的方法是使用 isinstance 和类型转换。如果我理解你的正确,你想要这个:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
你应该使用 isinstance
isinstance(...)
isinstance(object, class-or-type-or-tuple) -> bool
Return whether an object is an instance of a class or of a subclass thereof.
With a type as second argument, return whether that is the object's type.
The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
isinstance(x, A) or isinstance(x, B) or ... (etc.).
您可能需要 isinstance
内置函数:
self.data = data if isinstance(data, list) else self.parse(data)
好,很好。我只是把这个例子和一个元组而不是一个文件名放在一起,但这很容易。谢谢大家。
class MyData:
def __init__(self, data):
self.myList = []
if isinstance(data, tuple):
for i in data:
self.myList.append(i)
else:
self.myList = data
def GetData(self):
print self.myList
a = [1,2]
b = (2,3)
c = 我的数据(一)
d = 我的数据(b)
c.GetData()
d.GetData()
[1, 2]
[2, 3]
你为什么不去更pythonic?
class AutoList:
def __init__(self, inp):
try: ## Assume an opened-file...
self.data = inp.read()
except AttributeError:
try: ## Assume an existent filename...
with open(inp, 'r') as fd:
self.data = fd.read()
except:
self.data = inp ## Who cares what that might be?
if inp.hasattr('read') and callable(inp.read): self.data = inp.read()
。第二种情况会更加复杂。最后,所有这些可能会花费更多的 CPU。毫不奇怪,python 手册支持 EAFP:docs.python.org/3.6/glossary.html#term-eafp
我首选的解决方案是:
class MyClass:
_data = []
__init__(self,data=None):
# do init stuff
if not data: return
self._data = list(data) # list() copies the list, instead of pointing to it.
然后使用 MyClass()
或 MyClass([1,2,3])
调用它。
希望有帮助。快乐编码!
_data
和 self._data
我们都不清楚。
不定期副业成功案例分享
@staticmethod
,因为此示例中的__init__
几乎没有什么用处,而目标是首先使用例如fromfilename
?