python - 类装饰器中的 lambda 上的 inspect.getcode 返回 PY3 中的整个类
问题描述
我们终于从 Python 2.7.13 迁移到 Python 3.6.6,我发现 inspect.getcode 的一些奇怪行为与 Python 2.7.13 相比,在 Python 3.6.6 中的行为非常不同。
我们有许多具有装饰器的类,该装饰器定义是否允许此类的实例执行特定操作。此检查由管理这些装饰器的单独模块完成,而不是由类本身完成。为了避免意外更改,我们使用单元测试来记录装饰器内的代码,并将其与基准进行比较。代码用inspect.getcode() 记录。如果装饰器使用外部方法,那么 PY2 和 PY3 捕获的结果是相同的 - 方法的代码。但是,如果装饰器使用 lambda 表达式,结果就完全不同了:
- PY2 返回整个装饰器,而不仅仅是 lambda 表达式,但这对于单元测试来说并不是真正的问题。
- 然而,PY3 返回装饰器和整个类 - 这是一个问题,因为现在类中的任何更改都会破坏这个单元测试。
我在 inspect.getsource() 中找不到任何可以解释这一点的记录差异。我在这里遗漏了什么还是这是一个错误?我知道有一些简单的解决方法,例如我可以检查返回的字符串并剪辑 lambda 下的所有内容 - 但我宁愿理解为什么会发生这种情况。
这是一些可以直接在 PY2 和 PY3 中运行的示例代码(需要六个)。这段代码是从头开始编写的以演示问题 - 请不要担心装饰器的实际实现或其他实现方式。
checks = dict() # a mapping of class to check function
# the class decorator
def check(check_function):
def wrap(wrapped):
checks[wrapped] = check_function
return wrapped
return wrap
# example 1: check with lambda. This demonstrates the problem
@check(lambda cls: True)
class checked_with_lambda(object):
def run(self):
pass
# example 2: check with external helper. This works as expected
def my_checker(obj):
return True
@check(my_checker)
class checked_with_function(object):
def run(self):
pass
# this is how the check would be used. This works fine
c = checked_with_lambda()
if checks[type(c)](c):
c.run()
# this is what a unit test would do
# (I have added the looks-like line to show that the check function is actually what I am expecting)
import six, inspect
for cls, check in six.iteritems(checks):
print('Check for "{}":\n'
'looks like: "{}"\n'
'code: "{}"\n'.format(cls.__name__,
repr(check),
inspect.getsource(check)))
如果我使用 Python 2.7.13 运行此代码,则此结果符合预期:
Check for "checked_with_lambda":
looks like: "<function <lambda> at 0x0000025967FB2A58>"
code: "@check(lambda cls: True)
"
Check for "checked_with_function":
looks like: "<function my_checker at 0x0000025967FB2AC8>"
code: "def my_checker(obj):
return True
"
在 PY3 中,这看起来完全不同 - 请注意第一个类的检查功能代码如何包含整个类代码:
Check for "checked_with_lambda":
looks like: "<function <lambda> at 0x000002A2B295BC80>"
code: "@check(lambda cls: True)
class checked_with_lambda(object):
def run(self):
pass
"
Check for "checked_with_function":
looks like: "<function my_checker at 0x000002A2B295BD08>"
code: "def my_checker(obj):
return True
"
解决方案
AFAICT 这是 Python 3 中的故意更改。 inspect.getsource
是基于行的,在 Python 3 中,inspect.BlockFinder
帮助程序类检测 lambda 定义所在的行是装饰器并返回整个装饰对象。Python 2 没有这样做。你只需要解决这个问题。
推荐阅读
- php - 将结果拆分为两列并保留字母顺序
- java - 编译错误..无法将类解析为类型
- cordova - Ionic v1 - 播放计费非消耗品购买批准事件在重新启动时自动调用应用程序
- docker - Vue 中的热重载在 Docker 容器中不起作用
- python - TKinter/TTK:可点击区域中的多个元素
- eclipse - Eclipse 错误 - 为 C:\WINDOWS\system32\config\systemprofile 创建目录失败
- jquery - jqGrid 5.5.0 中的错误
- android - ApiException:10:在 com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(com.google.android.gms:play-services-base@@17.1.0:4)
- react-native - 反应原生将实时记录数据转换为可写的base64?
- csv - 使用类和 csv 文件进行转换