【问题标题】:Is it pythonic to have lots of constructors in a class?在一个类中有很多构造函数是pythonic吗?
【发布时间】:2014-07-29 08:08:12
【问题描述】:

Point class in Geopy中,我们可以看到很多不同的方式来调用它的构造函数:

>>> p1 = Point(41.5, -81, 0)
>>> p2 = Point(latitude=41.5, longitude=-81)
>>> p3 = Point([41.5, -81, 0])
>>> p4 = Point((41.5, -81))
>>> p5 = Point(p4)
>>> p6 = Point('41.5,-81.0')
>>> p7 = Point('41.5 N -81.0 W')
>>> p8 = Point('-41.5 S, 81.0 E, 2.5km')
>>> p9 = Point('23 26m 22s N 23 27m 30s E 21.0mi')
>>> p10 = Point('''3 26' 22" N 23 27' 30" E''')

我想知道这是好还是坏的做法,代码味道,是否违反了某些 Pythonic 习语。实际上,这种担忧适用于任何 OO 语言,但我专门询问 python

【问题讨论】:

  • 您可以根据需要实现任意数量的构造函数。我不会说 10 是一个荒谬的数字。使用继承和/或拆分您的类,因为这有助于降低单个类的复杂性。最重要的是,过分担心其他人可能认为或可能不认为是 Python 的不是 Python 的。

标签: python oop constructor ooad


【解决方案1】:

有可能

def __init__(self, *args, **kwargs):
    # "args" holds a list of positional arguments,
    # "kwargs" holds a dictionary of keyword arguments

    if len(args) == 1 and not kwargs:
        if isinstance(args[0], str):
            if re.match(args[0], some_regex):
                ... # etc

是代码味道

这使得初始化代码很糟糕,很难将数据安全地传递给构造函数,并使构造函数意外地处理格式错误的数据。

在我看来,它违反了 Python 之禅 (import this) 中的第 1、2、3、5、7、10、12 和 17 条。所以我会说它违反了 Pythonic 习语。

• 美丽胜于丑陋。 • 显式优于隐式。 • 简单胜于复杂。 • 平面优于嵌套。 • 可读性很重要。 • 错误绝​​不应无声无息地过去。 • 面对模棱两可的情况,拒绝猜测的诱惑。 • 如果实现难以解释,那就是个坏主意。

查看链接代码,我明白了

POINT_PATTERN = re.compile(r"""
    .*?
    (?P<latitude>
      (?P<latitude_direction_front>[NS])?[ ]*
        (?P<latitude_degrees>-?%(FLOAT)s)(?:[%(DEGREE)sD\*\u00B0\s][ ]*
        (?:(?P<latitude_arcminutes>%(FLOAT)s)[%(PRIME)s'm][ ]*)?
        (?:(?P<latitude_arcseconds>%(FLOAT)s)[%(DOUBLE_PRIME)s"s][ ]*)?
        )?(?P<latitude_direction_back>[NS])?)
    %(SEP)s
    (?P<longitude>
      (?P<longitude_direction_front>[EW])?[ ]*
      (?P<longitude_degrees>-?%(FLOAT)s)(?:[%(DEGREE)sD\*\u00B0\s][ ]*
      (?:(?P<longitude_arcminutes>%(FLOAT)s)[%(PRIME)s'm][ ]*)?
      (?:(?P<longitude_arcseconds>%(FLOAT)s)[%(DOUBLE_PRIME)s"s][ ]*)?
      )?(?P<longitude_direction_back>[EW])?)(?:
    %(SEP)s
      (?P<altitude>
        (?P<altitude_distance>-?%(FLOAT)s)[ ]*
        (?P<altitude_units>km|m|mi|ft|nm|nmi)))?
    .*?$
""" % {
    "FLOAT": r'\d+(?:\.\d+)?',
    "DEGREE": format.DEGREE,
    "PRIME": format.PRIME,
    "DOUBLE_PRIME": format.DOUBLE_PRIME,
    "SEP": r'\s*[,;/\s]\s*',
}, re.X)

我个人印象深刻。使这种大小的 Regex 具有如此可读性并不容易。但这很难解释或简单


有很好的选择

您可以使用staticmethodclassmethod 构造函数。

class Rectangle:
    def __init__(self, a=0, b=0):
        self.a = a # measured in meters
        self.b = b # measured in meters

    @classmethod
    def as_square(cls, a):
        return cls(a, a)

    @classmethod
    def from_inches(cls, a_inches, b_inches):
        inch = 0.0254 # in meters
        return cls(a_inches * inch, b_inches * inch)

    def __repr__(self):
        templ = "<Rectangle: {self.a:.3f} x {self.b:.3f}>"
        return templ.format(self=self)

这允许您从数据中进行实例化,同时仍将不同的方法分开。

>>> Rectangle(10, 20)
<Rectangle: 10.000 x 20.000>
>>> Rectangle.as_square(100)
<Rectangle: 100.000 x 100.000>
>>> Rectangle.from_inches(2, 4)
<Rectangle: 0.051 x 0.102>

另一方面

你看到int可以解析什么了吗?

from decimal import Decimal
int(2546)
#>>> 134
int(17657.342)
#>>> 17657
int(Decimal("134"))
#>>> 2546
int("5443")
#>>> 5443
int("543", 34)
#>>> 5919
int("໖᭗")
#>>> 67

说我喜欢什么,我不会让很多人相信这没有先例。

【讨论】:

  • 很好,很好。顺便说一句,def __init__(latitude=0, longitude=0): 行不应该包含self 并阅读def __init__(self, latitude=0, longitude=0): 吗?
  • 您答案的静态方法部分是我发现最强大的部分。我习惯在 Numpy 和 .NET 中经常看到这一点
  • @Veedrac 但是__init__ 真的是静态的吗?我会期待@staticmethod 装饰器。你能在没有所有... 的情况下制作工作代码吗?到目前为止,我看到您有一些很有前途的想法,但我到目前为止还没有得到。
  • @Veedrac 不用担心,我今天从你那里学到了一些东西。编辑了您的示例以使其可运行,我希望我得到了正确的结果。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 2011-12-19
  • 2017-11-29
  • 2021-08-10
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 2017-11-07
相关资源
最近更新 更多