python 构造函数重载_关于python:如何基于参数类型重载 __init__方法?

假设我有一个类,它有一个名为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

[collapse title=""]
  • 酷!在哪里可以看到@classmethod在引擎盖下究竟做了什么?
  • python.org/download/releases/2.2.3/descrintro是一个很好的源代码。
  • 您在哪里定义了cls()的行为?
  • @Ajay请参阅此问题以获得澄清
  • 为什么不使用@staticmethod,因为本例中的__init__做得很漂亮,没有什么有用的,当目标是首先使用fromfilename时?
  • 我为此奋斗了一段时间,最后我创建了一个基类和两个子类,每个子类都有不同的初始参数列表。这对我来说更易读。谢谢你的鼓励!
[/collapse]

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值