【问题标题】:Parametrized inheritance in Python?Python中的参数化继承?
【发布时间】:2018-01-10 14:47:24
【问题描述】:

我很好奇是否有更好的方法来实现这样的目标? 我的意图是避免不必要的样板。提供的示例显然很简单,足以让其他人理解我的想法。

def create_parametrized_class(animal):
    class SomeClass:
        def __init__(self, name):
            self.name = name

        def __str__(self):
            return "{}: {}".format(animal, self.name)

    return SomeClass

class Cat(create_parametrized_class("Cat")):
    pass

class Dog(create_parametrized_class("Dog")):
    pass

cat = Cat("Micka")
dog = Dog("Rex")
assert str(cat) == "Cat: Micka", "Cats..."
assert str(dog) == "Dog: Rex", "Dogs..."

【问题讨论】:

  • 这是什么 Python 版本?看起来像 3.6+,因为 Martijn Pieters 的回答就足够了,但你应该为可能遇到同样问题的其他人标记它。 __init_subclass__ 在 3.6 之前无法使用。
  • 好的,我添加了 Python 3.6 标签。我不知道 init_subclass 所以我不确定它是什么 Python 版本。
  • 你应该总是用你正在使用的版本标记它,这样人们就可以根据它提供答案。您可以使用python --versionpython3 --version 查看版本
  • 嗯,我知道我使用的是什么版本 :) 问题是我不希望解决方案是 3.6 特定的。但由于我使用的是 3.6,所以我不介意。不过还是谢谢你的提示。

标签: python inheritance python-3.6


【解决方案1】:

我将假设 type(self).__name__ 在这里不够用(对于您的两个示例类,该值等于您传入的参数值)。

要在类定义时设置每个类的值,从 Python 3.6 开始,您可以使用 __init_subclass__ classmethod

class Animal:
    def __init_subclass__(cls, animal_name, **kw):
        super().__init_subclass__(**kw)
        self._animal_name = animal_name

    def __str__(self):
        return "{}: {}".format(self._animal_name, self.name)


class Cat(Animal, animal_name='Cat'):
    pass

class Dog(Animal, animal_name='Dog'):
    pass

为所有新子类调用__init_subclass__,并且您在class Subclass(...) 行中指定的任何参数都将传递给该方法,让您参数化该特定子类。

【讨论】:

  • @MarkusMeskanen:现在在答案中明确指出。
  • 另外,答案并不重要,但既然我们已经在 3.6 上,为什么不return f'{self._animal_name}: {self.name}' ;)
  • @MarkusMeskanen:因为我关注的是问题的主要方面;如果语法更改对代码的风格有实质性的改进,我会尝试只引入它们。当然,我也更喜欢新的 f'...' 语法,但我觉得它会分散答案的重点。 :-)
  • 很公平,一次一个新功能。 (虽然我个人认为 f 弦是个例外,但它们太诱人了)。
  • @MarkusMeskanen:它们对 Python 的执行速度更快,并将槽和表达式放在一个地方(更容易让大脑看到什么在哪里)。
【解决方案2】:

我认为你最好使用简单的继承和类变量:

class Animal(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '{}: {}'.format(type(self).name, self.name)

class Cat(Animal):
    name = 'Cat'

class Dog(Animal):
    name = 'Dog'

这对我来说看起来更干净(特别是如果您的变量不止一个),并且使用较少的“高级”功能(即阅读您的代码的人不必在谷歌上搜索 __init_subclasses__ 的工作原理)。

它也适用于 Python 2 和 3:

>>> cat = Cat('Micka')
>>> print(cat)
'Cat: Micka'

如果您要使用classproperty,您甚至可以将其默认为类的名称,并且可以用一个简单的类变量覆盖。这会阻止您对类和实例变量使用相同的名称,因此您必须使用类似 animal_name 的名称:

class Animal(object):

    @classproperty
    def animal_name(cls):
        return cls.__name__

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '{}: {}'.format(self.animal_name, self.name)


class Cat(Animal):
    pass

class Dog(Animal):
    animal_name = 'Doggo'

使用示例:

>>> dog = Dog('Mike')
>>> cat = Cat('Bob')
>>> str(dog)
'Doggo: Mike'
>>> str(cat)
'Cat: Bob'

【讨论】:

  • 是的,看起来不错。谢谢你。我更愿意保留 Python 3.6 解决方案,因为我的初衷是学习新东西。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 2014-01-07
  • 2020-05-16
  • 2011-04-22
  • 1970-01-01
相关资源
最近更新 更多