请考虑以下代码:
import re
def qcharToUnicode(s):
p = re.compile(r"QChar\((0x[a-fA-F0-9]*)\)")
return p.sub(lambda m: '"' + chr(int(m.group(1),16)) + '"', s)
def fixSurrogatePresence(s) :
'''Returns the input UTF-16 string with surrogate pairs replaced by the character they represent'''
# ideas from:
# http://www.unicode.org/faq/utf_bom.html#utf16-4
# http://stackoverflow.com/a/6928284/1503120
def joinSurrogates(match) :
SURROGATE_OFFSET = 0x10000 - ( 0xD800 << 10 ) - 0xDC00
return chr ( ( ord(match.group(1)) << 10 ) + ord(match.group(2)) + SURROGATE_OFFSET )
return re.sub ( '([\uD800-\uDBFF])([\uDC00-\uDFFF])', joinSurrogates, s )
现在我的问题可能反映了C / C ++的思维方式(而不是“Pythonic”方式),但我很好奇:
我想知道在p
中的qcharToUnicode
和SURROGATE_OFFSET
中编译的RE对象joinSurrogates
的评估是否会在每次调用相应函数时进行或者只在定义点一次?我的意思是在C / C ++中,可以将值声明为static const
,并且编译将(IIUC)使构造仅发生一次,但在Python中我们没有任何此类声明。
这个问题在编译的RE对象的情况下更为相关,因为构造这样一个对象的唯一原因似乎是避免重复编译,正如Python RE HOWTO所说:
您是否应该使用这些模块级函数,还是应该自己获取模式并调用其方法?如果你是 在循环中访问正则表达式,预编译它将节省一些函数调用。
...如果在每次函数调用时都进行编译,那么这个目的就会失败。我不想将符号p
(或SURROGATE_OFFSET
)放在模块级别,因为我只想将其可见性限制在相关函数中。
解释器也可以通过启发式确定特定符号指向的值是常量(并且仅在特定函数中可见),因此无需在下一个函数中重建?此外,这是由语言还是依赖于实现来定义的? (我希望我不要求太多!)
一个相关的问题是关于lambda m
中函数对象qcharToUnicode
的构造 - 它是否也像def
声明的其他命名函数对象一样定义了一次?
答案 0 :(得分:3)
简单的答案是,在编写代码时,代码将在每次函数调用时重复执行。对于您描述的情况,Python中没有隐式缓存机制。
你应该摆脱谈论“声明”的习惯。函数定义实际上也只是一个普通的语句,所以我可以编写一个循环来重复定义相同的函数:
for i in range(10):
def f(x):
return x*2
y = f(i)
在这里,我们将承担在每次循环运行时创建函数的成本。时间显示此代码在前一代码的大约75%的时间内运行:
def f(x):
return x*2
for i in range(10):
y = f(i)
优化RE情况的标准方法是您已知道将p
变量放在模块范围内,即:
p = re.compile(r"QChar\((0x[a-fA-F0-9]*)\)")
def qcharToUnicode(s):
return p.sub(lambda m: '"' + chr(int(m.group(1),16)) + '"', s)
您可以使用诸如在变量前加上“_”之类的约定来表示它不应该被使用,但通常如果您没有记录它,人们将不会使用它。使RE函数本地化的一个技巧是使用关于默认参数的结果:它们与函数定义同时执行,因此您可以这样做:
def qcharToUnicode(s, p=re.compile(r"QChar\((0x[a-fA-F0-9]*)\)")):
return p.sub(lambda m: '"' + chr(int(m.group(1),16)) + '"', s)
这将允许您进行相同的优化,但在匹配功能方面也有一点灵活性。
正确思考函数定义还可以让您不再将lambda
视为与def
不同。唯一的区别是def
还将函数对象绑定到名称 - 创建的基础对象是相同的。
答案 1 :(得分:1)
是的,他们是。假设re.compile()有副作用。每次对p进行赋值时,即每次调用包含所述赋值的函数时,都会发生这种副作用。
这可以验证:
def foo():
print("ahahaha!")
return bar
def f():
return foo()
def funcWithSideEffect():
print("The airspeed velocity of an unladen swallow (european) is...")
return 25
def funcEnclosingAssignment():
p = funcWithSideEffect()
return p;
a = funcEnclosingAssignment()
b = funcEnclosingAssignment()
c = funcEnclosingAssignment()
每次调用封闭函数(类似于你的qcharToUnicode)时,都会打印语句,显示正在重新评估p。
答案 2 :(得分:1)
Python是一种脚本/解释语言......所以是的,每次调用函数时都会进行赋值。解释器只解析一次代码,生成Python字节码。下次调用此函数时,它将被编译为Python VM字节码,因此该函数将被简单执行。
每次都会调用re.compile,就像在其他语言中一样。如果您想模仿静态初始化,请考虑使用全局变量,这样它只会被调用一次。更好的是,您可以使用静态方法和静态成员(类而非实例成员)创建一个类。
您可以使用Python中的dis模块检查所有这些。所以,我只是将你的代码复制并粘贴到teste.py模块中。
>>> import teste
>>> import dis
>>> dis.dis(teste.qcharToUnicode)
4 0 LOAD_GLOBAL 0 (re)
3 LOAD_ATTR 1 (compile)
6 LOAD_CONST 1 ('QChar\\((0x[a-fA-F0-9]*)\\)')
9 CALL_FUNCTION 1
12 STORE_FAST 1 (p)
5 15 LOAD_FAST 1 (p)
18 LOAD_ATTR 2 (sub)
21 LOAD_CONST 2 (<code object <lambda> at 0056C140, file "teste.py", line 5>)
24 MAKE_FUNCTION 0
27 LOAD_FAST 0 (s)
30 CALL_FUNCTION 2
33 RETURN_VALUE