假设我有一个类,该类具有一个名为data的成员,该成员是一个列表。

我希望能够使用例如文件名(包含用于初始化列表的数据)或实际列表进行初始化的类。

您的技术是什么是这样做的吗?

您是否只是通过查看__class__来检查类型?

我可能会缺少一些技巧吗?

我' m习惯于C ++,其中按参数类型进行重载很容易。

评论

可能重复什么是在Python中具有多个构造函数的python干净方法?

@,反之亦然? (我的意思是这是一个比较老的问题)

@Wolf我不会说两者之间哪个是更好的话题,但是当更新的问题质量更高/具有更好的答案/以更广泛适用的方式涵盖该话题时,较旧的问题通常会因为较新的问题而被封闭。 br />

#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 楼

您应该使用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.).


#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