使用exec生成代码的习惯用法

你好,
在我编写的Python程序中,我需要动态生成
函数[*]并将它们存储在刻录中. eval()不能对我有用
因为函数定义是陈述而不是表达式,所以
我正在使用Exec.目前,我想出了以下内容
工作:
def build_func(args):
代码""" def foo(...)..."""
d = {}
Globals()中的执行代码,D
返回D ['foo']
我的问题是,考虑到我确实需要代码生成[*] -
"有什么干净的方法可以做到吗?"另外,如果我替换会发生什么
Globals()无?
此外,我发现缩进是一个问题
结构体.是否有一种可行的方法可以将代码置于
build_func,而不是在第0列上?
提前致谢
埃利
[*]我知道每次代码生成问题都会出现
建议有一种更好的方法来实现这一目标,而无需使用Exec,
评估等.但是就我而言,出于太长时间的布置,我
确实需要用大量硬编码来生成非平凡的功能
绩效的动作.而且没有安全问题
任何.如果有人对该应用程序非常感兴趣,我会
详细说明.

# 回答1


Eliben aécrit:
(剪)
只是为了表明事情很清楚:您确实知道您可以动态构建
没有执行的功能,对吗?
只是出于好奇:您能告诉更多有关您的用例的信息
是什么使简单的封闭不是选择?
# 回答2


6月20日,上午9:17,布鲁诺·戴斯图里尔(Bruno Desthuilliers)<布鲁诺(Bruno).
42.desthuilli ...@werfeburburo.invalidwrote:
(剪)
只是为了表明事情很清楚:您确实知道您可以动态构建
没有执行的功能,对吗?
是的,但是这样做的其他选择要少得多
比执行灵活.
只是出于好奇:您能告诉更多有关您的用例的信息
是什么使简单的封闭不是选择?
好的.
我在嵌入式编程领域工作,这是主要用途之一
我有Python(和以前的Perl)正在为
控制嵌入式系统.通信协议通常是
临时消息(标题,页脚,数据,CRC)构建在串行顶部
通信(RS232).
到达的数据包具有已知格式.例如(yamlish
句法):
packet_length:10
字段:
- 名称:标题
偏移:0
长度:1
- 名称:time_tag
偏移:1
长度:1
变换:Val * 2048
单位:MS
- 名称:计数器
偏移:2
长度:4
字节-MSB-First:true
- 名称:bitmask
偏移:6
长度:1
bit_from:0
BIT_TO:5
...
这是部分功能显示.字段已定义偏移和
长度只能长几位,可以定义
转换和单元,以方便显示.
我有一个程序,应该从串行端口接收此类数据包
并以表格形式显示其内容.我希望用户能够
在类似于上面的文件中指定其数据包的格式.
现在,在此代码的先前版本中,用perl编写了
提取场阀的过程 数据包的UE非常
效率低下.我已经使用动态生成的过程对其进行了重写
对于每个字段,都可以对其数据进行严格的编码访问.例如:
def get_counter(数据包):
数据=数据包[2:6]
data.redverse()
返回数据
这给了我巨大的加速,因为现在每个领域都有其特定的
坐在dict中的功能很快提取了该领域的数据
从给定的数据包.
现在,我正在重写Python,并想知道
使用exec的惯用方法(在perl,eval()替换eval和exec
Python).
埃利
# 回答3


伊本写道:
exec"如果1:" + code.rstrip()
彼得
# 回答4


6月20日,8:03 AM,Eliben 是的,但是这样做的其他选择要少得多
比执行灵活.
好的.
我在嵌入式编程领域工作,这是主要用途之一
我有Python(和以前的Perl)正在为
控制嵌入式系统.通信协议通常是
临时消息(标题,页脚,数据,CRC)构建在串行顶部
通信(RS232).
到达的数据包具有已知格式.例如(yamlish
句法):
packet_length:10
字段:
- 名称:标题
偏移:0
长度:1
- 名称:time_tag
偏移:1
长度:1
变换:Val * 2048
单位:MS
- 名称:计数器
偏移:2
长度:4
字节-MSB-First:true
- 名称:bitmask
偏移:6
长度:1
bit_from:0
BIT_TO:5
...
这是部分功能显示.字段已定义偏移和
长度只能长几位,可以定义
转换和单元,以方便显示.
我有一个程序,应该从串行端口接收此类数据包
并以表格形式显示其内容.我希望用户能够
在类似于上面的文件中指定其数据包的格式.
现在,在此代码的先前版本中,用perl编写了
从数据包中提取现场值的过程非常
效率低下.我已经使用动态生成的过程对其进行了重写
对于每个字段,都可以对其数据进行严格的编码访问.例如:
def get_counter(数据包):
数据=数据包[2:6]
data.redverse()
返回数据
这给了我巨大的加速,因为现在每个领域都有其特定的
坐在dict中的功能很快提取了该领域的数据
从给定的数据包.
目前尚不清楚为什么通用版本如此慢,除非您
仅提取几个选定的字段,而不是全部.你能发布一个吗
示例您曾经在不执行的情况下写它以澄清在哪里
效率低下来自?
乔治
# 回答5


在6月20日,5:03*上午,Eliben fwiw,当我对动态编码有类似的挑战时,我只是
生成一个PY文件,然后导入它.这项技术很好
因为也可以与Pyrex或Psyco一起使用.
另外,上面的代码可以简化为:get_counter = lambda
数据包:数据包[5:1:-1]
由于python的功能调用很昂贵, 您也可以提高速度
通过一次解析多个字段:
标头,timetag,counter =解析(数据包)
雷蒙德
# 回答6


Eliben aécrit:
(剪)
只是为了表明事情很清楚:您知道您可以在没有执行的情况下动态构建功能,对吗?
是的,但是这样做的其他选择要少得多
比执行灵活.
让我们来看看...
出于好奇:您能否详细介绍一下您的使用Caseand是什么使简单的封闭不是选择?

好的.
我在嵌入式编程领域工作,这是主要用途之一
我有Python(和以前的Perl)正在为
控制嵌入式系统.通信协议通常是
临时消息(标题,页脚,数据,CRC)构建在串行顶部
通信(RS232).
好的
好的
好的
好的.因此,如果我正确正确,则将函数的代码构建为字符串
基于YAML规范.
如果是这样,好吧,我想不出任何更好的东西[1] - 至少 *如果 *
动态生成的过程确实是更好的性能,
在Python中可能 *是否 *.
[1]除了使用Compile构建使用该函数的代码对象
主体,然后使用此代码实现函数对象,但我不是
当然,它是否会为您提供更多的性能.我是个性的
更喜欢这个,因为我发现它更明确,可读性,但YMMV.
好吧...到目前为止,使用Exec的最柔软的方法是避免使用它 -
除非它是工作的正确工具! - )

# 回答7


fwiw,当我对动态编码有类似的挑战时,我只是
我想这与在概念上使用exec的使用没有多大不同
等级.当您真的只需要一个时,Exec也许更合适
一次功能,而不是相关功能的整个文件.
好的,但这只是一个示威.实际功能是
复杂足以不适合单个表达.
埃利
# 回答8


[1]除了使用Compile构建使用该函数的代码对象
如何编译比执行更可读性 - 不需要额外的
步 ?无论如何,您还是动态生成代码.
埃利
# 回答9


6月20日,下午3:19,乔治·萨基斯(George Sakkis)<乔治·萨克(George.Sak)...
目前尚不清楚为什么通用版本如此慢,除非您
仅提取几个选定的字段,而不是全部.你能发布一个吗
示例您曾经在不执行的情况下写它以澄清在哪里
效率低下来自?
乔治
通用版本必须在运行时做出很多决定,基于
在格式规范上.
从规格中提取偏移,提取长度.是msb-吗
第一的 ?然后反向.需要具体的位吗?如果是这样,请做一点
操作.钻头应该逆转吗?等等
动态生成的功能不必做出任何决定 -
一切都硬编码,因为这些决定已经完成
在编译时.这可以节省大量的访问和条件,
并导致加速.
我想这没什么不同 来自LISP宏的ENT-做出决定
在编译时而不是运行时间和节省性能.
埃利
# 回答10


在6月20日,下午3:44,Eliben 通用版本必须在运行时做出很多决定,基于
在格式规范上.
从规格中提取偏移,提取长度.是msb-吗
第一的 ?然后反向.需要具体的位吗?如果是这样,请做一点
操作.钻头应该逆转吗?等等
因此,您是在说例如"如果do_reverse:data.reverse()"是
*比" data.reverse()"慢得多?我希望检查
与反向相比,布尔的真理可以忽略不计
本身.您是否尝试将所有检查转换为与身份比较
没有任何 ?我的意思是替换每个" if compile_time_condition:"循环中

compile_time_condition = compile_time_condition或无
因为我在some_loop中:
如果Compile_time_condition无:
...
很难相信身份检查的开销是
可比较(更高更高)与循环的主体
比"通行证"更复杂的事物.
乔治
# 回答11


在20 Juin,21:41,Eliben 比exec的编译如何更易读 -
使用编译和函数(),您明确启动了一个新的
功能对象,在使用Exec时,您要依靠副作用.
好吧...你的方式:
d = {}
Globals()中的执行代码,D
返回D ['foo']
我的方式:
返回函数(compile(代码,'','exec'),Globals())
就我所关注的是,这少了两个步骤 - 但是YMMV当然! - )
确实是的.这可能是或不正确的事情,但是
是一个不同的问题(我实际上无法回答).
# 回答12


在20 Juin,21:44,Eliben (剪)
通用版本必须在运行时做出很多决定,基于
在格式规范上.
从规格中提取偏移,提取长度.
导入操作员
变形金刚= []
transformers.append(operator.itemgetter(slice(at at.offset,format.offset)
+format.length))))
if格式.msb_first:
Transformer.Append(反向)
等等.... python函数是对象,您可以定义自己的可呼叫
(IE:功能类型)类型,您可以定义匿名单一表达
使用lambda的功能,功能也关闭,以便他们可以携带
他们定义的环境,实施部分申请
(使用闭合或可呼叫的对象)是微不足道的(并且在
自2.5 fwiw以来,stdlib功能模块,嗯...定义序列
Transormer功能的函数也不是问题.并将其应用于
您的数据bytestring只是微不足道的:
def apply_transformers(数据,transormers):
用于变压器中的变压器:
数据=变压器(数据)
返回数据
....并且不一定在整个绩效范围内(在这里您都会有)
确定两种解决方案,确定).
不,但是一系列可呼叫的对象也没有.决定是
带走了你有t的地方 他必要的背景,并在某个地方应用
别的.动态生成/编译代码是一种可能的解决方案,
但不是唯一的一个.
主要区别是LISP宏不是原始字符串构建的,
但是作为头等对象.我发现这种方法更灵活
并且更容易维护,但是再次在这里,YMMV.
无论如何,即使(正如您现在可能已经注意到的那样)我是其中之一
"有一个比eval-exec的路,"人民,我想你可能会
(根据解决方案和现实生活数据的基准,取决于基准)
这里有效的用例 - 如果正确封装了此部分,
您可以一直从当前的解决方案开始(因此您可以使其正常工作),
然后最终切换实施,如果值得额外的话
努力...
只是我的2美分.事实是,只要它起作用并且是
可维护,然后谁在乎...
# 回答13


因此,您是在说例如"如果do_reverse:data.reverse()"是
也有dict访问(提取格式参数,这样
作为长度和偏移)的格式,这些格式不存在.此外,
字段通常很小,因此相反的情况相对便宜.
埃利
# 回答14


d = {}
在IRC的家伙的一些帮助下,我意识到您的方式没有
照着做.它创建一个函数,当称为时,会在
Globals().这不是我需要的.
埃利
# 回答15


在6月20日,下午2:44,Peter Otten <__ Pete ...@web.dewrote:
exec"如果1:" + code.rstrip()
彼得
为什么这里需要"如果"?我为我做了.
def make_func():
代码=""""
def foo(数据包):
返回ORD(数据包[3]) + 256 * ORD(数据包[4])
"""
d = {}
exec code.strip()in Globals(),d
返回D ['foo']
没有.strip.这无效:
Trackback(最近的最新电话):
文件" exec_code_generation.py",第25行,
foo = make_func()
文件" exec_code_generation.py",第20行,在make_func中
Globals()中的执行代码,D
文件" ",第2行
def foo(数据包):
^
缩进:意外缩进
# 回答16


Eliben写道:exec"如果1:" + code.rstrip()peter

为什么这里需要"如果"?我为我做了.
如果代码包含多行:
.... 返回 """
.... x = 42
....如果x 0:
....打印x
....""""
...
42
Trackback(最近的最新电话):
文件" ",第1行,<模块>
文件" ",第2行
如果x 0:
^
缩进:意外缩进
您当然可以将代码分为线,计算
第一条非白线,从所有线中删除凹痕,然后
重新加入.
彼得

# 回答17


在6月21日,上午8:52,彼得·奥特(Peter Otten)<__ pete ...@web.dewrote:
如果代码包含多行:
... 返回 """
... x = 42
...如果x 0:
...打印x
...""""
... >> exec"如果1:\ n" + f().rstrip()
42
Trackback(最近的最新电话):
文件" ",第1行,<模块>
文件" ",第2行
如果x 0: ^
缩进:意外缩进
我懂了.就我而言,我仅通过" exec"评估功能定义,因此
我只需要删除第一行,其他行可以是
缩进了,因为无论如何它们都处于新范围.您的建议有效
对于任意代码,不仅是功能定义.这很好
用"如果1:" :-)(_@_ _)的技巧
在6月21日,2:02*PM,Eliben 我懂了.就我而言,我仅通过" exec"评估功能定义,因此
我只需要删除第一行,其他行可以是
缩进了,因为无论如何它们都处于新范围.您的建议有效
对于任意代码,不仅是功能定义.这很好
用"如果1:"的技巧:-)
您是否真的介绍了您的代码?还是你只是在这个
猜测的假设?
# 回答18


我懂了.就我而言,我仅通过" exec"评估功能定义,因此
您是否真的介绍了您的代码?还是你只是在这个
猜测的假设?
首先,我看到您的问题和
您引用的文字.在那儿?或者您是随机选择一个帖子
发布您的问题?
第二,是的 - 我已经介绍了我的代码.
第三,这是一条非常典型的酷刑路径
询问代码生成.几乎所有社区都是如此
除了LISP,也许.您必须说服每个人
做您做的事的真正理由.回复的简单规范
当您获得代码生成时,您的问题不起作用.我想知道
为什么这样.实际上有多少人被坏代码"烧死"
一代技术,还有多少只是在伪装" goto是邪恶的"
因为这是公认的话.这是一个有趣的观点
深思.
埃利
# 回答19


在6月21日,9:40*上午,Eliben 首先,我看到您的问题和
您引用的文字.在那儿?或者您是随机选择一个帖子
发布您的问题?
第二,是的 - 我已经介绍了我的代码.
第三,这是一条非常典型的酷刑路径
询问代码生成.几乎所有社区都是如此
除了LISP,也许.您必须说服每个人
做您做的事的真正理由.回复的简单规范
当您获得代码生成时,您的问题不起作用.我想知道
为什么这样.实际上有多少人被坏代码"烧死"
一代技术,还有多少只是在伪装" goto是邪恶的"
因为这是公认的话.这是一个有趣的观点
深思.
并没有那么多的人被烧毁,但是就像
Goto,有99%的时间有更好的选择.脱离我的顶部
头,在C.L.Py中的两个重复线程与动态代码有关
一代和评估是:
- 询问如何动态生成变量名称("对于我
Xrange(10):exec'x%d =%d'%(i,i)"),而不是使用常规
字典.
- 使用乐趣 ction名称而不是实际函数对象和
调用eval(),不知道函数是一流的对象(或
甚至不熟悉这意味着什么).
因此,即使您的用例属于Dynamic的特殊1%
代码生成是合理的,您应该期望人们质疑它
默认.
乔治
# 回答20

br br xhitdeyde***********@gmail.com写道:
...
.... <带有操作列表> ...的示例
加入"非执行"操作替代方案.提取
逐件看起来可能很慢.你可以找出
在大多数endian中所在的地方(通常是一种方式),
并特别对待其他任何人(用一个
采用操作).最好的优化来自理解
特定情况下的规律性;他们很少来自
生成隐藏规律性并取决于编译器的代码
推断这种规律性.
您是否尝试过这样的事情(解决方案的草图)?:
导入结构
从函数引入部分导入
导入操作员
类解码器(对象):
def __init __(自我,打开包装,处理器,决赛):
''当然,这可能只是将YAML带进去''''
self.unpack =打开包装
self.processors =处理器
self.length = struct.calcsize(打开包装)
self. -finals =决赛
def数据包(自我,数据):
parts = list(struct.unpack(self.unpack,data))
对于n,行动,导致自我处理器:
如果结果没有:
parts.append(action(parts [n]))
别的:
零件[n] = action(parts [n])
返回元组(self. -finals中的n零件[n])#或命名tuple ...
def _bits(from_bit,mask,v):
返回(v> from_bit)和蒙版
您的示例延长了一点:
fmt =解码器(' (3,部分(_bits,5,0x3),无),),,
(3,局部(operator.iand,0x80),无),),,
(3,部分(operator.iand,0x1f),' - '),,
(6,bool,' - ')],
[0,1,2,3,6,7,4,5])
打印fmt.packet(source.read(fmt.length))
- 斯科特·戴维·丹尼尔斯(Scott David Daniels)
感谢您在这篇文章中的所有答复.只是总结,我想
发布我写的一件代码,以封装在此中的函数创建
方法:
def create_function(代码):
"""创建并返回代码中定义的函数.
"""
m = re.match('\ s*def \ s+([a-za-z _] \ w*)\ s*\(',代码)
如果m:
func_name = m.group(1)
别的:
没有返回
d = {}
exec code.strip()in Globals(),d
返回D [func_name]
尽管一开始的" def"匹配看起来有些伪劣
首先,它在所有情况下都应起作用.
埃利
# 回答21


Eliben aécrit:
在IRC的家伙的一些帮助下,我意识到您的方式没有
照着做.它创建一个函数,当称为时,会在
Globals().这不是我需要的.
我可能在争论,标志等中弄乱了几件事 - 我
很少使用Compile()和函数().关键是没有
需要任何额外的步骤.
# 回答22


LE 2008年6月23日星期一09:22:29 Bruno Desthuilliers,VousAvezécrit*:
我可能把几件事弄乱了 在参数,标志等中 - 我
很少使用Compile()和函数().关键是 *没有
需要任何额外的步骤.
在函数类型的参数列表中,代码对象首先是
预计将直接使用Python类型"代码"直接创建(无exec -eval)
(以类型为codeType或new.code找到).
在[24]中:类型.
类型:类型
基类:<类型'type'>
字符串表格:<类型'代码'>
名称空间:交互式
DOCSTRING:
代码(argcount,nlocals,stackSize,flags,codeString,常数,名称,
varnames,filename,name,firstlineno,lnotab [,freevars [,
cellvars]]))
创建一个代码对象.不是为了胆小的人.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^因为
即使看起来更"面向对象",我也不确定它实际上是好的
原始问题的解决方案.我认为这些界面不是
替换快速评估的成语,但更旨在使大规模
代码生成程序面向对象,靠近Python内部.
AFAIK,我看到的代码生成的唯一用例
python中的合法时间,实际上是执行代码的程序
生成,即从文本输入(应用程序)中解析和编译代码
Buillders).
如果代码生成不是最好的,我看不到任何性能问题
这可以解释这样的选择,除了误解
"汇编"在python中的意思是什么,只是不要使用它,使用封闭或
可召唤的实例,有很多方法可以实现这一目标.
- -
_____________
Maric Michaud
# 回答23


Maric Michaud Aécrit:
我可能会在参数,标志等中弄乱了几件事 - 很少使用Compile()和function().关键是它没有任何额外的步骤.

在函数类型的参数列表中,代码对象首先是
预计将直接使用Python类型"代码"直接创建(无exec -eval)
这就是汇编的回报.但确实,重新阅读编译的文档
小心,恐怕它返回的代码对象可能无法使用
我想的方式.我的错.

对不起

(剪)

# 回答24


在6月21日,7:52*AM,Peter Otten <__ Pete ...@web.dewrote:
如果代码包含多行:
... * * 返回 """
... * * x = 42
... * *如果x 0:
... * * * * * *打印x
... * *""""
... >> exec"如果1:\ n" + f().rstrip()
42
Trackback(最近的最新电话):
*文件" ",第1行,<模块>
*文件" ",第2行
* *如果x 0:
* * ^
缩进:意外缩进
您当然可以将代码分为线,计算
第一条非白线,从所有线中删除凹痕,然后
重新加入.
textwrap.dedent将为您做所有事情...
迈克尔·福德(Michael Foord)http://www.ironpythoninaction.com/
# 回答25


如果代码生成不是最好的,我看不到任何性能问题
当我们讨论汇编在Python中的主题时,我是
不确定我完全理解了之间的区别 een编译(.pyc)
代码和执行代码.执行代码也将其转换为字节码
即它将像编译D代码一样高效吗?
埃利
# 回答26


伊本写道:
这取决于实现.
我是
执行之前,CPYTHON始终将其编译为字节码.没有
替代执行路径.
# 回答27


LE 2008年6月24日,星期二07:18:47 Eliben,VousAvezécrit*:
当我们讨论汇编在Python中的主题时,我是
不确定我完全了解编译(.pyc)之间的区别
代码和执行代码.执行代码也将其转换为字节码
即它将像编译D代码一样高效吗?
是的,完全相同,当脚本时,cpython总是解释编译的代码
例如执行,例如,它由解释器解析/编译为字节码
执行之前. .pyc/pyo文件只是导入时创建的缓存
是时候避免耗时的解析阶段了.
- -
_____________
Maric Michaud
# 回答28


由于没有人提到文本wrap.
旧的"如果1:"技巧,我想我应该这样做. :)
# 回答29


在6月23日,6:44 AM,Eliben 其实这是行不通的,因为Globals()返回的范围
create_function被定义,而不是调用.所以如果我想放置
create_function在某些utils文件中,其生成的代码不会是
能够使用文件中的辅助函数
create_function.
埃利

标签: python

添加新评论