Python - 如何强制使用工厂方法来实例化对象?

2024-02-05

我有一组相关的类,它们全部继承自一个基类。我想使用工厂方法来实例化这些类的对象。我想这样做是因为这样我可以在将对象返回给调用者之前将对象存储在以类名为键的字典中。然后,如果有对特定类的对象的请求,我可以检查我的字典中是否已存在该对象。如果没有,我将实例化它并将其添加到字典中。如果是这样,那么我将从字典中返回现有对象。这实际上会将我模块中的所有类变成单例。

我想这样做是因为它们继承的基类会对子类中的函数进行一些自动包装,并且我不希望这些函数被多次包装,这就是当前如果两个对象创建同一个类。

我能想到的唯一方法是检查堆栈跟踪__init__()基类的方法,该方法将始终被调用,并且如果堆栈跟踪未显示创建对象的请求来自工厂函数,则抛出异常。

这是一个好主意吗?

Edit:这是我的基类的源代码。有人告诉我,我需要找出元类来更优雅地完成此任务,但这就是我现在所拥有的。所有 Page 对象都使用相同的 Selenium Webdriver 实例,该实例位于顶部导入的驱动程序模块中。该驱动程序的初始化成本非常高——它是在第一次创建 LoginPage 时初始化的。初始化后initialize()方法将返回现有的驱动程序而不是创建新的驱动程序。这个想法是用户必须首先创建一个登录页面。最终将定义数十个 Page 类,单元测试代码将使用它们来验证网站的行为是否正确。

from driver import get_driver, urlpath, initialize
from settings import urlpaths

class DriverPageMismatchException(Exception):
    pass

class URLVerifyingPage(object):
    # we add logic in __init__() to check the expected urlpath for the page
    # against the urlpath that the driver is showing - we only want the page's
    # methods to be invokable if the driver is actualy at the appropriate page.
    # If the driver shows a different urlpath than the page is supposed to
    # have, the method should throw a DriverPageMismatchException

    def __init__(self):
        self.driver = get_driver()
        self._adjust_methods(self.__class__)

    def _adjust_methods(self, cls):
        for attr, val in cls.__dict__.iteritems():
            if callable(val) and not attr.startswith("_"):
                print "adjusting:"+str(attr)+" - "+str(val)
                setattr(
                    cls,
                    attr,
                    self._add_wrapper_to_confirm_page_matches_driver(val)
                )
        for base in cls.__bases__:
            if base.__name__ == 'URLVerifyingPage': break
            self._adjust_methods(base)

    def _add_wrapper_to_confirm_page_matches_driver(self, page_method):
        def _wrapper(self, *args, **kwargs):
            if urlpath() != urlpaths[self.__class__.__name__]:
                raise DriverPageMismatchException(
                    "path is '"+urlpath()+
                    "' but '"+urlpaths[self.__class.__name__]+"' expected "+
                    "for "+self.__class.__name__
                )
            return page_method(self, *args, **kwargs)
        return _wrapper


class LoginPage(URLVerifyingPage):
    def __init__(self, username=username, password=password, baseurl="http://example.com/"):
        self.username = username
        self.password = password
        self.driver = initialize(baseurl)
        super(LoginPage, self).__init__()

    def login(self):
        driver.find_element_by_id("username").clear()
        driver.find_element_by_id("username").send_keys(self.username)
        driver.find_element_by_id("password").clear()
        driver.find_element_by_id("password").send_keys(self.password)
        driver.find_element_by_id("login_button").click()
        return HomePage()

class HomePage(URLVerifyingPage):
    def some_method(self):
        ...
        return SomePage()

    def many_more_methods(self):
        ...
        return ManyMorePages()

如果一个页面被实例化了几次,那也没什么大不了的——方法只会被包装几次,并且会进行一些不必要的检查,但一切仍然有效。但如果一个页面被实例化数十、数百或数万次,那就很糟糕了。我可以在每个页面的类定义中放置一个标志,并检查方法是否已经被包装,但我喜欢保持类定义纯粹和干净的想法,并将所有的骗局推到页面的深处。我的系统没有人可以看到它并且它可以正常工作。


在 Python 中,几乎不值得尝试“强制”任何事情。无论你想出什么办法,有人都可以通过猴子修补你的类、复制和编辑源代码、摆弄字节码等来绕过它。

因此,只需编写您的工厂,并将其记录为获取类实例的正确方法,并期望使用您的类编写代码的任何人都能够理解 TOOWTDI,并且不要违反它,除非她真的知道自己在做什么并且愿意弄清楚并处理后果。

如果您只是想防止事故发生,而不是故意“误用”,那就另当别论了。事实上,这只是标准的契约设计:检查不变量。当然此时,SillyBaseClass已经搞砸了,想要修复已经来不及了,你能做的就是assert, raise, log,或任何其他合适的内容。但这就是您想要的:这是应用程序中的逻辑错误,唯一要做的就是让程序员修复它,所以assert可能正是您想要的。

So:

class SillyBaseClass:
    singletons = {}

class Foo(SillyBaseClass):
    def __init__(self):
        assert self.__class__ not in SillyBaseClass.singletons

def get_foo():
    if Foo not in SillyBaseClass.singletons:
        SillyBaseClass.singletons[Foo] = Foo()
    return SillyBaseClass.singletons[Foo]

如果你真的想阻止事情发展到这一步,你can早些时候检查不变量,在__new__方法,但除非“SillyBaseClass搞砸了”相当于“发射核武器”,何必呢?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python - 如何强制使用工厂方法来实例化对象? 的相关文章

  • Python 类型提示 Dict 语法错误 可变默认值是不允许的。使用“默认工厂”

    我不知道为什么解释器会抱怨这个类型的字典 对于这两个实例 我得到一个 不允许可变默认值 使用默认工厂 语法错误 我使用的是 python 3 7 3 from dataclasses import dataclass from typing
  • 我应该使用 Python 双端队列还是列表作为堆栈? [复制]

    这个问题在这里已经有答案了 我想要一个可以用作堆栈的 Python 对象 使用双端队列还是列表更好 元素数量较少还是数量较多有什么区别 您的情况可能会根据您的应用程序和具体用例而有所不同 但在一般情况下 列表非常适合堆栈 append is
  • Django Rest Framework 是否有第三方应用程序来自动生成 swagger.yaml 文件?

    我有大量的 API 端点编写在django rest framework并且不断增加和更新 如何创建和维护最新的 API 文档 我当前的版本是 Create swagger yaml文件并以某种方式在每次端点更改时自动生成 然后使用此文件作
  • 使用主题交换运行多个 Celery 任务

    我正在用 Celery 替换一些自制代码 但很难复制当前的行为 我期望的行为如下 创建新用户时 应向tasks与交换user created路由键 该消息应该触发两个 Celery 任务 即send user activate email
  • 从Django中具有外键关系的两个表中检索数据? [复制]

    这个问题在这里已经有答案了 This is my models py file from django db import models class Author models Model first name models CharFie
  • 为什么 web2py 在启动时崩溃?

    我正在尝试让 web2py 在 Ubuntu 机器上运行 所有文档似乎都表明要在 nix 系统上运行它 您需要下载源代码并执行以下操作 蟒蛇 web2py py 我抓住了source http www web2py com examples
  • PyQt 使用 ctrl+Enter 触发按钮

    我正在尝试在我的应用程序中触发 确定 按钮 我当前尝试的代码是这样的 self okPushButton setShortcut ctrl Enter 然而 它不起作用 这是有道理的 我尝试查找一些按键序列here http ftp ics
  • MongoEngine 查询具有以列表中指定的前缀开头的属性的对象的列表

    我需要在 Mongo 数据库中查询具有以列表中任何前缀开头的特定属性的元素 现在我有一段这样的代码 query mymodel terms term in query terms 并且这会匹配在列表 term 上有一个项目的对象 该列表中的
  • 打印包含字符串和其他 2 个变量的变量

    var a 8 var b 3 var c hello my name is var a and var b bye print var c 当我运行程序时 var c 会像这样打印出来 hello my name is 8 and 3 b
  • Python 3:将字符串转换为变量[重复]

    这个问题在这里已经有答案了 我正在从 txt 文件读取文本 并且需要使用我读取的数据之一作为类实例的变量 class Sports def init self players 0 location name self players pla
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • python的shutil.move()在linux上是原子的吗?

    我想知道python的shutil move在linux上是否是原子的 如果源文件和目标文件位于两个不同的分区上 行为是否不同 或者与它们存在于同一分区上时的行为相同吗 我更关心的是如果源文件和目标文件位于同一分区上 shutil move
  • Python - 如何确定解析的 XML 元素的层次结构级别?

    我正在尝试使用 Python 解析 XML 文件中具有特定标记的元素并生成输出 excel 文档 该文档将包含元素并保留其层次结构 我的问题是我无法弄清楚每个元素 解析器在其上迭代 的嵌套深度 XML 示例摘录 3 个元素 它们可以任意嵌套
  • Python GTK+ 画布

    我目前正在通过 PyGobject 学习 GTK 需要画布之类的东西 我已经搜索了文档 发现两个小部件似乎可以完成这项工作 GtkDrawingArea 和 GtkLayout 我需要一些基本函数 如 fillrect 或 drawline
  • 如何使用 Python 3 检查目录是否包含文件

    我到处寻找这个答案但找不到 我正在尝试编写一个脚本来搜索特定的子文件夹 然后检查它是否包含任何文件 如果包含 则写出该文件夹的路径 我已经弄清楚了子文件夹搜索部分 但检查文件却难倒了我 我发现了有关如何检查文件夹是否为空的多个建议 并且我尝
  • 找到一个数字所属的一组范围

    我有一个 200k 行的数字范围列表 例如开始位置 停止位置 该列表包括除了非重叠的重叠之外的所有类型的重叠 列表看起来像这样 3 5 10 30 15 25 5 15 25 35 我需要找到给定数字所属的范围 并对 100k 个数字重复该
  • Protobuf 如何编码 oneof 消息结构

    对于这个 python 程序 在编码时运行 protobuf 编码会给出以下输出 0a 10 08 7f8a 0104 08 02 10 0392 0104 08 02 10 03 18 01 我不明白的是为什么8a后面有一个01 为什么9
  • 重新分配唯一值 - pandas DataFrame

    我在尝试着assign unique值在pandas df给特定的个人 For the df below Area and Place 会一起弥补unique不同的价值观jobs 这些值将分配给个人 总体目标是使用尽可能少的个人 诀窍在于这
  • 根据 Pandas 中的列表选择数据框行的子集

    我有一个数据框df1并列出x In 22 import pandas as pd In 23 df1 pd DataFrame C range 5 B range 10 20 2 A list abcde In 24 df1 Out 24
  • 将索引与值交换的最快方法

    考虑pd Series s s pd Series list abcdefghij list ABCDEFGHIJ s A a B b C c D d E e F f G g H h I i J j dtype object 交换索引和值并

随机推荐