Python 是一种动态类型的语言,如何使它不必使用抽象 classes/inheritance?

How Python being a dynamically-typed language makes it unnecessary to use abstract classes/inheritance?

我在 python 中查找了设计模式,并在某篇文章中看到了以下句子:

"我们不使用继承来实现我们的抽象工厂,主要是因为Python是一种动态类型语言,因此不需要抽象类。"

代码示例:

class Dog:
    """One of the objects to be returned"""

    def speak(self):
        return "Woof!"

    def __str__(self):
        return "Dog"


class DogFactory:
    """Concrete Factory"""

    def get_pet(self):
        """Returns a Dog object"""
        return Dog()

    def get_food(self):
        """Returns a Dog Food object"""
        return "Dog Food!"


class PetStore:
    """ PetStore houses our Abstract Factory """

    def __init__(self, pet_factory=None):
        """ pet_factory is our Abstract Factory """

        self._pet_factory = pet_factory


    def show_pet(self):
        """ Utility method to display the details of the objects retured by the DogFactory """

        pet = self._pet_factory.get_pet()
        pet_food = self._pet_factory.get_food()

        print("Our pet is '{}'!".format(pet))
        print("Our pet says hello by '{}'".format(pet.speak()))
        print("Its food is '{}'!".format(pet_food))


#Create a Concrete Factory
factory = DogFactory()

#Create a pet store housing our Abstract Factory
shop = PetStore(factory)

#Invoke the utility method to show the details of our pet
shop.show_pet()

我理解我提供的例子,但我不明白这个说法,而且我在 python 中看到很多其他使用继承和抽象 类 实现设计模式的例子(例如:在这种情况下是抽象工厂)就像几乎所有其他语言一样,例如 java.

我非常希望能够解释该声明以及这两种方法(有和没有 abstract-classes/interfaces/inheritance)在使用代码示例时有何不同。

编辑:

问题: 具体来说,我想了解动态类型如何使生活更轻松,并通过说明来补偿上述代码示例中 inheritance/interfaces 的使用如果 inheritance/interfaces 用于实现给定示例的相同功能,代码示例(抽象工厂)会是什么样子。

谢谢。

如果必须用静态类型语言编写此代码,他们可能会编写一个名为 PetFactory 的基础 class,它具有两个函数,get_petget_food .

但是在动态类型语言中,函数(在本例中 PetStore(...))并不关心其参数的类型。相反,它使用“鸭子打字”

This term comes from the saying “If it walks like a duck, and it quacks like a duck, then it must be a duck.” (There are other variations).

Duck typing is a concept related to dynamic typing, where the type or the class of an object is less important than the methods it defines. When you use duck typing, you do not check types at all. Instead, you check for the presence of a given method or attribute.*

与动态类型语言的许多方面一样,它为您提供了更多的自由,但使事情变得不那么有条理。不需要继承特定的class或定义接口,但也更容易出错,增加运行时错误。

这里的关键是“PetFactory”。在上面的代码中,“PetFactory”没有技术意义。但从概念上讲,实际上是一个“接口”,我将其称为“PetFactory”。 “PetFactory”必须具有函数 get_petget_food.

如果要写一个新的“PetFactory”,不管他们是否在脑海中命名,他们都必须确定这个接口和它的要求。

事实上,它没有真正的名字,这使得它更难识别它是否存在或绕过它(当“界面”只是你正在工作的许多事情之一时,这就变得更相关了与).

但除此之外,要确定“PetFactory”的要求,必须跟踪多个函数。在这种情况下,它并不太难,但是如果有更多的功能,它会很快变得复杂。

相反,我们可以使用继承并按照以下方式做一些事情:

class PetFactory():
    def get_pet(self):
        """returns a pet"""
        pass

    def get_food(self):
        """returns a string defining the pet's food"""
        pass

class DogFactory(PetFactory):

    def get_pet(self):
        """Returns a Dog object"""
        return Dog()

    def get_food(self):
        """Returns a Dog Food object"""
        return "Dog Food!"

从技术的角度来看,添加这个 class 确实没有任何作用。但从概念上讲,“PetFactory”的存在现在是显而易见的,它的需求是明确的,而且它有一个技术名称。


随着 PEP 3119 的实施,python 现在也具有形式抽象基 classes。使用这些,新代码看起来像这样:

from abc import ABC, abstractmethod

class PetFactory(ABC):

    @abstractmethod
    def get_pet(self):
        """returns a pet"""
        pass

    @abstractmethod
    def get_food(self):
        """returns a string defining the pet's food"""
        pass

class DogFactory(PetFactory):

    def get_pet(self):
        """Returns a Dog object"""
        return Dog()

    def get_food(self):
        """Returns a Dog Food object"""
        return "Dog Food!"

这样做的好处是,它清楚地标记了 PetFactory class 的目的,并强制任何子class 对象在实例化之前实现所有抽象方法。它还允许进行类型检查:isinstance(PetFactory).