我希望能够使用例如文件名(包含用于初始化列表的数据)或实际列表进行初始化的类。
您的技术是什么是这样做的吗?
您是否只是通过查看
__class__
来检查类型?我可能会缺少一些技巧吗?
我' m习惯于C ++,其中按参数类型进行重载很容易。
#1 楼
获取“替代构造函数”的一种更整洁的方法是使用类方法。例如:>>> 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)
的问题在于,调用者无法告诉您,例如,即使类型不是基本字符串,您也应将其视为字符串(而不是其他序列)。也许调用者希望将同一类型用于不同目的,有时作为单个项目,有时作为一系列项目。显式消除所有疑问,并导致更健壮和更清晰的代码。评论
凉!我在哪里可以了解有关@classmethod的确切含义?
– Hamish Grubijan
2010-6-27 at 16:55
您在哪里定义了cls()的行为?
–阿贾伊
15年2月24日在17:22
@Ajay请参阅此问题以进行澄清
– KimAhlstrømMeyn Mathiassen
15年4月15日在6:39
为什么不使用@staticmethod,因为在本示例中__init__在目标是使用例如fromfilename放在首位?
–omni
18/09/10在12:55
我为此奋斗了一段时间,最后我创建了一个基类和两个子类,每个子类都有不同的init参数列表。这对我来说更具可读性。感谢您的灵感!
–veggiebenz
19年4月11日在17:28
#2 楼
很好的问题。我也解决了这个问题,虽然我同意“工厂”(类方法构造函数)是一个好方法,但我想提出另一个建议,我也发现它非常有用:这里是一个示例(这是一个
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时处理的代码
–脑风暴
2014年2月5日在1:39
@brainstorm我认为处理非无字符串的代码位于“#其余代码”中。 :-)
–姚斌
2015年1月30日15:16
当您想重载许多版本的构造函数时,可能使该解决方案不那么优雅的一件事是,例如,您想从整数,文件或字符串,或...或...构造对象。或...或...或...然后,您将得到很长的初始化参数列表。
–姚斌
2015年1月30日15:22
另一个问题是,作为调用者,我不知道除非阅读文档,否则应该使用哪些参数来构造对象。在上面的示例中,调用者可以同时提供str和文件名,但是仅考虑str,因为它在if语句层次结构中较高。文档可以提供帮助,但是最好设计界面时不要含糊不清。
–姚斌
15年1月30日在15:24
#3 楼
使用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__函数的想法很有趣,您能否解释一下其背后的原理?
– GoingMyWay
18年5月15日在7:42
MultipleMeta中的@GoingMyWay __prepare__方法返回一个MultiDict实例,以替换由clsdict在__new__方法中传递的Date类default__dict__属性。因此,它可以容纳具有相同名称“ __init__”的多功能,并且其值是一个MultiMethod实例,该实例在其_method属性中存储了不同的功能注释。您应该查看Python Cookbook了解更多详细信息。
–carton.swing
18年5月16日在1:31
@ carton.swing,哪个版本的python支持'委托'init?我尝试使用3.6.8,它抱怨TypeError:__init __()接受2个位置参数,但给出了3个。在我的代码中,它是init(self,x)和init(self,a,b),后者将从前者中调用。
–尤里·波兹尼亚克(Yuriy Pozniak)
19年11月4日23:28
@YuriyPozniak Python不会支持“委托”,它仅支持函数注释,您可以通过函数注释实现元类。您是否像Python Cookbook那样使用上述元类'MultipleMeta'?
–carton.swing
19年11月6日,3:08
@ carton.swing,感谢您的答复。不,我没有使用MultipleMeta。
–尤里·波兹尼亚克(Yuriy Pozniak)
19年11月6日在18:46
#4 楼
快速而又肮脏的修复程序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)。
– Vivek Ghaisas
14年6月19日在8:56
我相信这是最好的解决方案。如果您愿意看的话,我已经对其进行了详细介绍:stackoverflow.com/a/26018762/385025
– Fydo
2014年9月24日14:00
你不是想念__init__中的自我吗?并且您可能不想使用list作为输入名称,因为它遮盖了内置类型列表。
– Cleb
19年5月21日在20:20
这更像是变通方法,无法正确回答问题
–乔贝·露西娜(Joabe Lucena)
20-10-8在20:18
#5 楼
更好的方法是使用isinstance和类型转换。如果我理解正确,那么您需要这样做:def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
#6 楼
您应该使用isinstanceisinstance(...)
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.).
#7 楼
您可能需要isinstance
内置函数:self.data = data if isinstance(data, list) else self.parse(data)
评论
if else表达式仅适用于python 2.5(及更高版本)
–萌
08/09/26在20:15
#8 楼
好,很好。我只是将这个示例与一个元组(而不是文件名)放在一起,但这很容易。谢谢大家。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]
评论
不需要在init中使用所有代码-我将其缩短为仅一种类型转换,它执行相同的操作并且更加灵活。
–约翰·米利金(John Millikin)
08-09-26在20:22
在Python中,getter也几乎是不必要的。只需使用直接属性访问即可。如果需要执行更多操作,则可以使用property()将getter / setter隐藏在常规属性访问之后。
–托马斯·沃特斯
08年9月26日在20:30
我知道,但这违背了示例的目的。我只是试图显示如何使用两种不同的输入类型。元组/列表可能没有必要,但是如果是文件名则需要。我想这只是在回荡别人的话。我的榜样对我很有启发
– Baltimark
08/09/26在20:31
#9 楼
为什么不使用更多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?
评论
切勿通过使用try catch强制执行错误来控制执行流程。这是所有编程语言的标准规则。
–杰米·马歇尔(Jamie Marshall)
18年6月26日在0:59
不,在Python中经常(但并非总是如此)是相反的情况:stackoverflow.com/questions/12265451/…在这种情况下,这样做的确便宜得多。
– ankostis
18-6-27在15:26
我认为您误会了try / except的基础。它的基本工作方式与if语句完全不同,与其他流控制方法相比,处理的每个错误都具有非常高的cpu开销。您提供的链接建议您在可能发生各种错误的地方使用try / except-我同意。但是,这种情况与使用try / except完全不同,后者根据例外情况来更改程序的流程,您肯定会经常或有意发生这种情况。
–杰米·马歇尔(Jamie Marshall)
18年6月27日在17:04
不仅要考虑CPU时间(我非常了解stackoverflow.com/questions/2522005/…);这也是开发人员的时间,代码的简洁性供审阅者快速了解它,以及其他重要的编码样式问题。在上述第一种情况下,替代方案将是:如果inp.hasattr('read')和callable(inp.read):self.data = inp.read()。第二种情况将更加令人费解。最后,所有这些可能会花费更多的CPU。毫不奇怪,python手册认可EAFP:docs.python.org/3.6/glossary.html#term-eafp
– ankostis
18年6月28日在18:06
#10 楼
我的首选解决方案是: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。
–shuttle87
2015年8月3日23:56
在此示例中,_data类变量没有意义。也许您对“ _data”的含义有误解。
– miracle173
20年5月31日,0:15
此示例中的构造函数返回具有自己的_data列表或引用类变量_data中的公共列表的实例。一旦构造完成,就没有简单的方法让代码知道特定实例的行为。由于这两种行为完全不同,所以这似乎是一个糟糕的主意。
– gwideman
20年10月10日在5:14
评论
可能重复什么是在Python中具有多个构造函数的python干净方法?@,反之亦然? (我的意思是这是一个比较老的问题)
@Wolf我不会说两者之间哪个是更好的话题,但是当更新的问题质量更高/具有更好的答案/以更广泛适用的方式涵盖该话题时,较旧的问题通常会因为较新的问题而被封闭。 br />