RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1545567
Accepted
Aycon
Aycon
Asked:2023-10-13 11:25:52 +0000 UTC2023-10-13 11:25:52 +0000 UTC 2023-10-13 11:25:52 +0000 UTC

消除Python中的循环类引用依赖

  • 772

请看一下缩小后的代码:

from abc import ABC, abstractmethod

class AbstractProduct(ProductFactory, ABC):
    @abstractmethod
    def some_method(self):
        ''' Is abstract method '''

class Product1(AbstractProduct):
    def some_method(self):
        print('I am a Product1!')

class Product2(AbstractProduct):
    def some_method(self):
        print('I am a Product2!')

class ProductFactory:
    knowed_types = {
        0: Product1,
        1: Product2
    }

    @classmethod
    def create_product(cls, type_id:int):
        if type_id in cls.knowed_types:
            return cls.knowed_types[type_id]()

该代码不起作用,因为这些类循环相互引用。如何在不破坏当前代码逻辑的情况下打破循环依赖?该片段仅用于演示目的。我不需要专门用这段代码来解决问题。我想知道一般情况下,Python是否有内置的方法来消除这样的循环依赖?例如,在 Django 中,您可以将类名括在引号中以推迟依赖关系解析。在 C++ 中,您可以在文件的开头声明函数签名,然后声明其实现。纯Python中有类似的机制吗?

python
  • 2 2 个回答
  • 129 Views

2 个回答

  • Voted
  1. andreymal
    2023-10-13T18:41:39Z2023-10-13T18:41:39Z

    tl;dr两种主要方式:

    • 隔离功能体内的依赖关系;
    • 使用带有对象名称的字符串而不是对象本身。

    在今天的问题中,出于某种原因,作者没有提到他昨天提到的细微差别 - 他希望方法create_product不仅在类中ProductFactory,而且在类中AbstractProduct(“可用性和代码可维护性之间的权衡”),为此,他尝试进行继承,从而获得了循环依赖。

    在昨天的(现已删除)问题中,我已经提出通过用类中的简单代理方法替换完全不合逻辑的继承来解决循环依赖AbstractProduct,但由于某种原因,问题的作者对此并不满意,他断然拒绝解释为什么。ProductFactorycreate_productAbstractProduct

    对 Python 不太熟悉的人可能会有一个问题:为什么这会起作用,即使在我修改的代码中ProductFactory它是在类声明之前提到的ProductFactory?事实上,Python(像许多其他脚本语言一样)不会检查函数体中代码的正确性 - 您可以在其中编写任何内容,只要语法正确即可。Python 只会在调用函数时立即开始查找变量ProductFactory- 并且在调用函数时,类ProductFactory已经创建,因此一切正常,并且循环依赖已成功克服。

    隔离函数和方法内的依赖关系是处理循环依赖关系的一种方法。如果该类ProductFactory在另一个文件中,那么您import也可以在方法中编写此类;幸运的是,Python 允许您随时随地进行导入 - 类似地,导入只会在调用函数时发生。

    from abc import ABC, abstractmethod
    
    class AbstractProduct(ABC):
        @abstractmethod
        def some_method(self):
            ''' Is abstract method '''
    
        @staticmethod
        def create_product(type_id:int):
            # Суть — вот в этом методе
            # Если ProductFactory из другого файла, то можно сделать импорт тут:
            # from some_module import ProductFactory
            return ProductFactory.create_product(type_id)
    
    class Product1(AbstractProduct):
        def some_method(self):
            print('I am a Product1!')
    
    class Product2(AbstractProduct):
        def some_method(self):
            print('I am a Product2!')
    
    class ProductFactory:
        knowed_types = {
            0: Product1,
            1: Product2
        }
    
        @classmethod
        def create_product(cls, type_id:int):
            if type_id in cls.knowed_types:
                return cls.knowed_types[type_id]()
    
    # Теперь можно не использовать ProductFactory напрямую, если так больше нравится
    product = AbstractProduct.create_product(1)
    product.some_method()
    

    为了多样化,我还将添加一个“就像在 Django 中一样”的方法 - 使用带有对象名称的字符串而不是这些对象本身。这个想法是不立即使用依赖项,而是稍后使用该行中写入的标识符查找它。在下面的示例中knowed_types,我对其进行了更改,以便它存储类的名称,并且该方法create_product通过全局变量中的指定名称搜索这些类。根据具体情况,您可以使用字典等代替全局变量(但随后您需要编写填充此类字典的代码 - 在 Django 中,元类会执行此操作)。

    (如果对象位于不同的文件中,则可以在同一行中写入模块的路径,使用 importlib.import_module 函数导入它,然后使用 提取所需的对象getattr,但这在下面的示例中没有使用。)

    from abc import ABC, abstractmethod
    
    class ProductFactory:
        knowed_types = {
            # Строки вместо классов!
            0: "Product1",
            1: "Product2"
        }
    
        @classmethod
        def create_product(cls, type_id:int):
            if type_id in cls.knowed_types:
                # Получаем класс по его имени из какого-то хранилища
                # (в данном примере «хранилищем» служат глобальные переменные)
                product_type_name = cls.knowed_types[type_id]
                product_type = globals()[product_type_name]
                # И создаём экземпляр класса
                return product_type()
    
    # Я осознанно оставил наследование от ProductFactory здесь для примера,
    # но имейте в виду, что делать так некрасиво
    class AbstractProduct(ProductFactory, ABC):
        @abstractmethod
        def some_method(self):
            ''' Is abstract method '''
    
    class Product1(AbstractProduct):
        def some_method(self):
            print('I am a Product1!')
    
    class Product2(AbstractProduct):
        def some_method(self):
            print('I am a Product2!')
    
    product = AbstractProduct.create_product(1)
    product.some_method()
    
    • 1
  2. Best Answer
    Aycon
    2023-10-13T15:37:22Z2023-10-13T15:37:22Z

    请记住,总体投票统计数据并不总是反映答案的质量:投票支持开设俄语 StackOverflow

    我通过使用元类解决了这个问题type

    from abc import ABC, abstractmethod
    
    def custom_inherit(base, *parents, **kwds):
        return type(
            base.__name__,
            (*base.__mro__[1:-1], *parents),
            dict(base.__dict__),
            **kwds
        )
    
    class AbstractProduct:
        @abstractmethod
        def some_method(self):
            ''' Is abstract method '''
    
    class Product1(AbstractProduct):
        def some_method(self):
            ''' Concrete method of Product1 '''
            print('I am a Product1!')
    
    class Product2(AbstractProduct):
        def some_method(self):
            ''' Concrete method of Product2 '''
            print('I am a Product1!')
    
    class ProductFactory:
        knowed_types = {
            0: Product1,
            1: Product2
        }
    
        @classmethod
        def create_product(cls, type_id:int) -> AbstractProduct:
            if type_id in cls.knowed_types:
                return cls.knowed_types[type_id]()
    
    AbstractProduct = custom_inherit(AbstractProduct, ProductFactory, ABC)
    
    product = AbstractProduct.create_product(1)
    product.some_method()
    
    • -3

相关问题

  • 是否可以以某种方式自定义 QTabWidget?

  • telebot.anihelper.ApiException 错误

  • Python。检查一个数字是否是 3 的幂。输出 无

  • 解析多个响应

  • 交换两个数组的元素,以便它们的新内容也反转

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5