Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 想要一个行为类似于ABC但又是元类的类吗_Python_Oop - Fatal编程技术网

Python 想要一个行为类似于ABC但又是元类的类吗

Python 想要一个行为类似于ABC但又是元类的类吗,python,oop,Python,Oop,其中一个答案建议使用元类 class DogType(type): def __init__(cls, name, bases, attrs): """ this is called at the Dog-class creation time. """ if not bases: return #pick the habits of direct ancestor and extend it with

其中一个答案建议使用元类

class DogType(type):

    def __init__(cls, name, bases, attrs):
        """ this is called at the Dog-class creation time.  """

        if not bases:
            return

        #pick the habits of direct ancestor and extend it with 
        #this class then assign to cls.
        if "habits" in attrs:
            base_habits = getattr(bases[0], "habits", [])
            cls.habits = base_habits + cls.habits


class Dog(metaclass=DogType):
    habits = ["licks butt"]

    def __repr__(self):
        return f"My name is {self.name}.  I am a {self.__class__.__name__} %s and I like to {self.habits}"

    def __init__(self, name):
        """ dog instance can have all sorts of instance variables"""
        self.name = name

class Sheperd(Dog):
    habits = ["herds sheep"]

class GermanSheperd(Sheperd):
    habits = ["bites people"]

class Poodle(Dog):
    habits = ["barks stupidly"]

class StBernard(Dog):
    pass


for ix, cls in enumerate([GermanSheperd, Poodle, StBernard]):
    name = f"dog{ix}"
    dog = cls(name)
    print(dog)
但是,这会引发一个错误:

TypeError:元类冲突:派生类的元类必须 是其所有基元类的(非严格)子类

我喜欢这个解决方案,但是我还需要class
Dog
像元类一样工作,这样我就可以在
Dog
中定义抽象方法,这些方法需要在所有子类中传播。这可能是一种类似于
def bark()
的方法,所有子狗都需要实现它

如何使
Dog
既成为实现
DogType
功能的元类,又成为限制子类实例化和运行方式的抽象类?

如果查看
ABC
类的,这就是为什么你的例子会产生元类冲突,因此,从你的例子中,你可以通过

从abc导入ABCMeta,abstractmethod
类元(ABCMeta):
通过
类基类(元类=元):
@抽象方法
定义某物(自我):
通过
类派生类(基类):
定义某物(自我):
返回1
尝试:
基类()
除类型错误外:
通过
其他:
引发异常('元类失败')
派生类()

你可以看到这个程序运行得很好

@Mistermiagi的评论提供了一些思考的素材,也让我更好地理解了ABC的实施方式。“抽象到具体”的顺序似乎是这样的:

type --> ABCMeta --> ABC --> regular class --> regular subclass --> object
这就是说,如果
DogType
继承自
ABCMeta
而不是
type
,它仍然可以充当元类,同时允许其子类充当抽象基类,因为
ABCMeta
是任何
ABC
的元类。这使我们能够执行以下操作:


class DogType(ABCMeta):

    def __init__(cls, name, bases, attrs):
        """ this is called at the Dog-class creation time.  """

        if not bases:
            return

        #pick the habits of direct ancestor and extend it with
        #this class then assign to cls.
        if "habits" in attrs:
            base_habits = getattr(bases[0], "habits", [])
            cls.habits = base_habits + cls.habits


class Dog(metaclass=DogType):
    habits = ["licks butt"]

    def __repr__(self):
        return f"My name is {self.name}.  I am a {self.__class__.__name__} %s and I like to {self.habits}"

    def __init__(self, name):
        """ dog instance can have all sorts of instance variables"""
        self.name = name

    @abstractmethod
    def print_habits(self):
        for habit in self.habits:
            print(habit)


class Sheperd(Dog):
    habits = ["herds sheep"]
    def print_habits(self):
        for habit in self.habits:
            print(habit)

class GermanSheperd(Sheperd):
    habits = ["bites people"]
    def print_habits(self):
        for habit in self.habits:
            print(habit)

class Poodle(Dog):
    habits = ["barks stupidly"]
    def print_habits(self):
        for habit in self.habits:
            print(habit)

class StBernard(Dog):
    def print_habits(self):
        for habit in self.habits:
            print(habit)


for ix, cls in enumerate([GermanSheperd, Poodle, StBernard]):
    name = f"dog{ix}"
    print('\n', name)
    print(cls)
    dog = cls(name)
    dog.print_habits()

我知道在上面的代码中不清楚为什么我将
print\u habits
定义为
abstractmethod
,并在子类中重新实现它。我可以简单地在
Dog
中定义它一次,这很好,但是在我的用例中,有一些方法需要在所有
Dog
子类中强制执行,而有些则不需要

您只是想将元类应用于ABC类吗?也许。。。。你能详细说明一下吗?我对你想要实现什么有点困惑,因为你的示例代码没有方法或属性,但你需要的似乎是
ABCMeta
。你能澄清一下你真正想要做什么吗?元类可以完全重新定义类的行为,因此了解实际需要的行为是非常重要的。你能举例说明元类或类应该提供什么吗?你能修改一下描述吗?代码中没有多重继承,
元类
什么也不做。最简单的“解决方案”就是根本不使用
元类
。我需要做的基本上就是前面链接的问题中的内容。无论如何,我已经重新格式化了整个问题。也许现在更清楚了?如果不是的话,请告诉我……我想我可能在最初的问题中给我的班级起了混淆的名字。我刚刚编辑了原始问题中的类名,请看一看。我需要元类来定义抽象基类实现的行为,而不是相反。我可能有其他基于此的抽象基类metaclass@user32882看来我提供的答案应该有效,因为它与标准的ABC课程几乎相同?您希望实现/修改什么特定行为?请注意,元类与子类是正交的。
ABCMeta
ABC
之间的关系与
type
ABCMeta
之间的关系或
ABC
普通班之间的关系不同。前者是一个
isinstance
关系,其他的是
issubclass
关系。我只是稍微了解一下这个东西的窍门。。。谢谢你的宝贵意见