假设我有一个类,它有一个名为data的成员,这是一个列表。
我希望能够用文件名(其中包含初始化列表的数据)或实际列表初始化类。
你这样做的技巧是什么?
你只是通过查看__class__来检查类型吗?
我可能会错过什么把戏吗?
我习惯C++,其中参数类型超载很容易。
在python中使用多个构造函数的干净的python方法可能是什么的副本?
@反之亦然?(我的意思是这是老问题)
@Wolf我不会说哪一个是两个问题之间更好的主题,但是当新的问题质量更好/有更好的答案/以更广泛适用的方式涵盖主题时,旧的问题往往会以新问题的重复形式关闭。
获得"备用构造函数"的一个更简洁的方法是使用ClassMethods。例如:
>>> 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
', 'bar
', 'baz
']
>>> MyData.fromdict({"spam":"ham
- 酷!在哪里可以看到@classmethod在引擎盖下究竟做了什么?
- python.org/download/releases/2.2.3/descrintro是一个很好的源代码。
- 您在哪里定义了cls()的行为?
- @Ajay请参阅此问题以获得澄清
- 为什么不使用@staticmethod,因为本例中的__init__做得很漂亮,没有什么有用的,当目标是首先使用fromfilename时?
- 我为此奋斗了一段时间,最后我创建了一个基类和两个子类,每个子类都有不同的初始参数列表。这对我来说更易读。谢谢你的鼓励!
Excellent question. I've tackled this problem as well, and while I agree that"factories" (class-method constructors) are a good method, I would like to suggest another, which I've also found very useful:
Here's a sample (this is a read method and not a constructor, but the idea is the same):
[cc lang="python"]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时,您没有可以处理的代码。
@头脑风暴:我认为处理非无字符串的代码位于"代码的其余部分"。-)
当您想要重载许多版本的构造函数时,有一件事可能会使这个解决方案不那么优雅,例如您想要从整数、文件、字符串或…或者…或者…或者…或者…然后您将得到一个非常长的init参数列表。
另一个问题是,作为调用方,我不知道应该使用哪些参数来构造对象,除非我阅读文档。在上面的示例中,调用者可以同时提供str和文件名,但只考虑str,因为它在if语句层次结构中的级别更高。文档可以提供帮助,但是我们最好能在不产生歧义的情况下设计接口。
快速而肮脏的修复
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)。
这是我认为最好的解决办法。如果您想了解更多细节,我已经详细介绍了:stackoverflow.com/a/26018762/385025
更好的方法是使用isInstance和类型转换。如果我理解你是对的,你需要这个:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
很好地使用了basestring!
使用python3,可以像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类default__dict__属性。因此,它可以保存同名"EDOCX1"〔3〕的多个函数,其值是一个MultiMethod实例,该实例在其方法属性中存储有不同的函数注释。您应该查看python食谱了解更多详细信息。
你应该用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)
if-else表达式只能在Python2.5(及更高版本)中工作。
我的首选解决方案是:
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])调用它。
希望有帮助。快乐编码!
为什么投反对票?
我猜是因为我们两个都不清楚。
好的,太好了。我只是将这个示例与一个元组(不是文件名)混合在一起,但这很容易。谢谢大家。
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=MyDATA(a)
D= MyDATA(b)
C. GETDATA()
D. GETDATA()
〔1, 2〕
〔2, 3〕
在in it中不需要所有的代码——我把它简化为一个类型转换,它做同样的事情,并且更加灵活。
在Python中,getter也是不必要的。只需使用直接属性访问。如果需要做更多的工作,可以使用property()将getter/setter隐藏在普通属性访问之后。
我知道这一点,但这违背了示例的目的;我只是想演示如何使用两种不同的输入类型。可能不需要使用tuple/list,但如果它是一个文件名的话。不过,我想这与其他人所说的是一致的。我的例子对我很有启发性。
评论是@millikin
你为什么不去做更多的Python?
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?
永远不要通过使用try catch强制错误来控制执行流。对于所有编程语言来说,这是一个相当标准的规则。
不,在python中,通常(但不总是)是相反的:stackoverflow.com/questions/12265451/…在这种情况下,这样做确实要便宜得多。
我认为你误解了尝试的基础。它的基本工作方式与if语句非常不同,与其他流控制方法相比,处理的每个错误都有很高的CPU开销。您提供的链接建议您应该使用Try/Except,在可能发生各种错误的地方除外-我同意。但是,这种情况与使用Try/Except根据您确定经常或有意发生的异常更改程序流完全不同。
这不仅是需要考虑的CPU时间(我非常了解stackoverflow.com/questions/2522005/…);也是开发人员的时间,评审人员快速理解代码的简洁性,以及其他重要的编码风格问题。在上述第一种情况下,备选方案是:if inp.hasattr('read') and callable(inp.read): self.data = inp.read()。第二种情况会更加复杂。最后,所有这些可能会花费更多的CPU。毫不奇怪,python手册认可eafp:docs.python.org/3.6/glossary.html term eafp