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 编程可能不是我的菜 我创建了一个 tkinter GUI 它使用按钮回调来打开另一个窗口 其他搜索说这个窗口应该是顶级窗口 并且它工作得很好 但是每次按下按钮时它都会打开另一个相同的窗口 如据我所知 窗口 问题 如何测
  • 如何配置散景图以具有响应宽度和固定高度

    我使用通过组件功能嵌入的散景 实际上我使用 plot sizing mode scale width 它根据宽度进行缩放并保持纵横比 但我想要一个响应宽度但固定或最大高度 这怎么可能实现呢 有stretch both and scale b
  • 错误只有 size-1 数组可以转换为 Python 标量

    我有这个代码 for a in data X for i in a if not i isdigit x hash i data X column row x row row 1 row 0 column column 1 desired
  • OpenCV Python 删除图像中的某些对象

    我正在使用带有 opencv 和 numpy 的 python 来检测天文中的星星 例如这个1 https i stack imgur com AKwEJ jpg图片 使用模板匹配 我可以用阈值检测星星 单击 2 2 https i sta
  • Python - 为什么这段代码被视为生成器?

    我有一个名为 mb 的列表 其格式为 Company Name Rep Mth 1 Calls Mth 1 Inv Totals Mth 1 Inv Vol Mth 2 等等 在下面的代码中 我只是添加了一个包含 38 个 0 的新列表 这
  • Python NLP 英式英语与美式英语

    我目前正在用Python 进行NLP 工作 然而 在我的语料库中 既有英式英语也有美式英语 实现 实现 我正在考虑将英式英语转换为美式英语 但是 我没有找到一个好的工具 包来做到这一点 有什么建议么 我也找不到包 但试试这个 请注意 我必须
  • 检查对象数组中的多个属性匹配

    我有一个对象数组 它们都是相同的对象类型 并且它们有多个属性 有没有办法返回一个较小的对象数组 其中所有属性都与测试用例 字符串匹配 无论该属性类型是什么 使用列表理解all http docs python org 3 library f
  • 获取 zeep.exceptions.ValidationError:缺少与 suds 一起使用的方法的元素

    我正在移植开发的代码suds 0 6到zeep 2 4 0 以前的泡沫代码 client Client WSDLfile proxy proxy faults True config client factory create perUse
  • keras 预测内存交换无限期增加

    我使用keras实现了一个分类程序 我有一大组图像 我想使用 for 循环来预测每个图像 然而 每次计算新图像时 交换内存都会增加 我尝试删除预测函数内部的所有变量 并且我确信该函数内部存在问题 但内存仍然增加 for img in ima
  • 超时时杀死或终止子进程?

    我想尽可能快地重复执行子进程 然而 有时这个过程会花费太长的时间 所以我想杀死它 我使用 signal signal 如下所示 ppid pipeexe pid signal signal signal SIGALRM stop handl
  • 如何在 Numpy 中实现垃圾收集

    我有一个名为main py 它引用另一个文件Optimisers py它仅具有功能并用于for循环进入main py 这些函数都有不同的优化功能 This Optimisers py然后引用另外两个类似的文件 其中也只有函数 它们位于whi
  • 如何在 Tkinter 的 Button 小部件中创建多个标签?

    我想知道如何在 Tkinter 中创建具有多个标签的按钮小部件 如下图所示 带有子标签的按钮 https i stack imgur com jOZRw jpg正如您所看到的 在某些按钮中有一个子标签 例如按钮 X 有另一个小标签 A 我试
  • Scrapy - 不会爬行

    我正在尝试运行递归爬行 由于我编写的爬行不能正常工作 因此我从网络上提取了一个示例并进行了尝试 我真的不知道问题出在哪里 但是爬行没有显示任何错误 谁能帮我这个 另外 是否有任何逐步调试工具可以帮助理解蜘蛛的爬行流程 非常感谢任何与此相关的
  • Python:使用列表创建二叉搜索树

    我的代码的目标是从 txt 文件中获取每个单独的单词并将其放入列表中 然后使用该列表创建二叉搜索树来计算每个单词的频率 并按字母顺序打印每个单词及其频率 中的每个单词只能包含字母 数字 或 我无法用我的初学者编程知识来做的部分是使用我拥有的
  • 从 subprocess.Popen 获取整个输出

    我通过调用 subprocess Popen 得到了一个有点奇怪的结果 我怀疑这与我对 Python 的陌生有很大关系 args cscript USERPROFILE tools jslint js USERPROFILE tools j
  • 通过子类化 `io.TextIOWrapper` 来子类化文件 - 但它的构造函数有什么签名?

    我正在尝试子类化io TextIOWrapper下列的这个帖子 https stackoverflow com a 23796737 974555 虽然我的目标不同 以此开始 注意 动机 https stackoverflow com a
  • 带有整数的 np.sqrt 和 where 条件返回错误结果

    当我将 numpy sqrt 方法应用于带有 a 的整数数组时 我得到了奇怪的结果where健康 状况 见下文 对于整数 a np array 1 4 9 np sqrt a where a gt 5 Out 3 array 0 0 5 3
  • 提供节点名或服务名,或未知

    我收到这个 Python 错误 File Library Frameworks Python framework Versions 2 7 lib python2 7 urllib2 py line 1184 in do open rais
  • 如何获取所有Python标准库模块的列表?

    我想要类似的东西sys builtin module names标准库除外 其他不起作用的事情 sys modules 只显示已经加载的模块 sys prefix 包含非标准库模块并且似乎无法在 virtualenv 内工作的路径 我想要这
  • 使用Python的timeit获取“全局名称'foo'未定义”

    我想知道执行一条Python语句需要多少时间 所以我上网查了一下 发现标准库提供了一个名为timeit http docs python org library timeit html旨在做到这一点 import timeit def fo

随机推荐