我想定义一个函数,该函数的行为取决于是否调用
foo()
或已分配
bar = foo()
似乎是有可能的,如this question所示,但是那里描述的方法会引发错误:
TypeError: ord() expected string of length 1, but int found
,在str()
参数上使用ord()
的简单解决方案显然行不通。
我这样做的特殊原因是,foo()
创建一个全局变量baz
(如果被调用),而不创建一个全局变量(
在查看了问题的答案之后,我决定继续使用一个总会分配的全局“私有”变量_baz
。
答案 0 :(得分:3)
没有一种简单的方法可以让函数知道如何处理其结果。编写能改变这种行为的函数将是使人困惑的好方法。最好以简单明了的方式使用Python的功能。
答案 1 :(得分:1)
最终,如果在存储一个函数值和何时不存储两个值时需要两种不同的效果,则您更有可能需要两种不同的函数,然后相应地进行记录。
您评论说:“函数是数据(文件)读取器。如果在文件名上调用函数,则会创建一个全局数据变量,否则将为其分配。”-不,在“有时”以静默方式创建全局变量不是一个好的设计。如果您也需要,只需每次都创建全局变量。您最好使用全局thread-local variable,以便在每个线程中使用变量的不同值。或者,如果您使用的是异步代码,则为context-local变量。这样做可以减轻对代码中的全局变量的任何意外更改,期望它在调用函数后具有正确的值。而且,如您所见,更好的是,仅记录该函数返回读取的值,然后将其留给调用方以决定是否将该值分配给全局变量。
尽管有很多种方法可以检测是否存储了返回值,但它并非微不足道,也不是可以做的事情。例如,链接的问题使用Python语言的高级资源来检查被调用函数的编译代码-这种资源几乎从未在适合生产的代码中使用,即使在高级库或框架中也是如此,因为这种资源很容易被使用。要么太慢,要么突然而意外地中断。 (例如,如果在生产部署中配置了一些日志记录/回溯保存扩展名,该扩展名将在函数中插入包装器,那么它将被调用方函数误认为是框架中的被包装函数,而只是在生产环境中而不是在开发环境中机)。但更重要的是,链接的代码甚至没有开始来检查极端情况,例如,是否在存储值之前对值进行了转换(可能会在其上调用“替换”方法,或者任何其他字符串方法),或存储在列表或字典中,而不是存储在局部变量中。完全不可能使用这种方法涵盖所有可能的用例。
但是我要花一些时间来阐明一个答案,以澄清这根本没有用-如果您了解对象在Python中的工作方式,那么您可能会找到一种更好的方法来实现您所想的一切,而无需知道如果对象是“存储的”:
Python中的任何函数都将返回一个对象。即使对象是“无”。事实是在Python中,对象可以被视为“黑匣子”,直到它们被用于某物为止。实际上使用它们时-不仅存储在变量,列表中,作为字典中的值等,...发生这种情况时,它只是存储了一个“黑匣子”,而对象无法知道分配名称或添加到列表中(也许可以在本机代码中对钩子进行编码,以检查对象的引用计数是否增加,但是如果没有,则至少需要在Cython中执行此操作) C-非常非常虚张声势,足以说出“它无法察觉”。
但是,当一个对象被“使用”时,那就是另一回事了。当您想要检查对象“是”什么时,即使在交互式终端中,还是通过打印它,都将调用对象的__repr__
或__str__
方法。如果要对对象进行操作-尝试使用“ +”运算符或任何其他运算符将其与另一个对象相加,则会调用该对象中的特殊方法。
如果您尝试将对象状态保存到文件中,它将被序列化-根据所使用的序列化,该方法将调用pickle协议甚至__str__
所使用的方法。
因此,您可以专门化一个类并包装所有所需的“魔术”方法(在language Data Model中定义,名称用__
包围),以执行您想要执行的操作例如,当对象“不存储”时。
顺便说一句,如果在函数调用之后根本没有存储对象,那么甚至会在对象中调用一个非常简单的方法:将调用对象的__del__
方法。
因此,为了在不存储对象时简单起作用,可以使用自定义__del__
方法动态创建函数结果的子类,然后在其中执行操作。这仍然是在生产环境中可能会有一些异议的代码,但是比解释调用函数的操作码要简单得多。 但是当然,如果存储了对象,则在代码的稍后一点将其最终删除时,仍会触发__del__
方法。如果在__del__
之前调用了任何其他特殊方法,则可以设计一种机制来更改对象中的属性,如果是,则禁用副作用-但是如您所见,也不是小事。