Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用Python构造高效函数_Python - Fatal编程技术网

用Python构造高效函数

用Python构造高效函数,python,Python,我的编程几乎都是自学的,所以如果我在这个问题上的一些术语不正确,我会提前道歉。另外,我将用一个简单的例子来帮助说明我的问题,但请注意,这个例子本身并不重要,它只是希望让我的问题更清楚的一种方式 想象一下,我有一些格式不好的文本,有很多额外的空白,我想清理。因此,我创建了一个函数,该函数将用一个新行字符替换任何包含新行字符的空白字符组,并用一个空格替换任何其他空白字符组。函数可能如下所示 def白色空间清洁剂(文本): new\u line\u finder=re.compile(r“\s*\n\

我的编程几乎都是自学的,所以如果我在这个问题上的一些术语不正确,我会提前道歉。另外,我将用一个简单的例子来帮助说明我的问题,但请注意,这个例子本身并不重要,它只是希望让我的问题更清楚的一种方式

想象一下,我有一些格式不好的文本,有很多额外的空白,我想清理。因此,我创建了一个函数,该函数将用一个新行字符替换任何包含新行字符的空白字符组,并用一个空格替换任何其他空白字符组。函数可能如下所示

def白色空间清洁剂(文本):
new\u line\u finder=re.compile(r“\s*\n\s*”)
白空间查找器=重新编译(r“\s\s+”)
text=new\u line\u finder.sub(“\n”,text)
text=white\u space\u finder.sub(“,text)
返回文本
这很好,问题是现在每次我调用函数时,它都必须编译正则表达式。为了让它运行得更快,我可以这样重写它

new\u line\u finder=re.compile(r“\s*\n\s*”)
白空间查找器=重新编译(r“\s\s+”)
def白色空间清洁剂(文本):
text=new\u line\u finder.sub(“\n”,text)
text=white\u space\u finder.sub(“,text)
返回文本

现在正则表达式只编译一次,函数运行速度更快。在两个函数上使用
timeit
,我发现第一个函数每个循环需要27.3µs,第二个函数每个循环需要25.5µs。这是一个小的速度提升,但是如果函数被调用数百万次,或者有数百个模式而不是2个模式,这可能会非常重要。当然,第二个函数的缺点是它污染了全局名称空间,使代码可读性降低。是否有某种“Pythonic”方法可以在函数中包含对象(如编译后的正则表达式),而不必每次调用函数时都重新编译它?

您可以使用静态函数属性保存编译后的正则表达式。这个例子做了类似的事情,在一个函数属性中保留一个转换表

def static_var(varname, value):
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

@static_var("complements", str.maketrans('acgtACGT', 'tgcaTGCA'))
def rc(seq):
    return seq.translate(rc.complements)[::-1]
保留要应用的元组列表(正则表达式和替换文本);似乎没有迫切的必要单独命名每一个

finders = [
    (re.compile(r"\s*\n\s*"), "\n"),
    (re.compile(r"\s\s+"), " ")
]
def white_space_cleaner(text):
    for finder, repl in finders:
        text = finder.sub(repl, text)
    return text
您还可以合并
functools.partial

from functools import partial
replacers = {
    r"\s*\n\s*": "\n",
    r"\s\s+": " "
}
# Ugly boiler-plate, but the only thing you might need to modify
# is the dict above as your needs change.
replacers = [partial(re.compile(regex).sub, repl) for regex, repl in replacers.iteritems()]


def white_space_cleaner(text):
    for replacer in replacers:
        text = replacer(text)
    return text

您可以将正则表达式编译放入函数参数中,如下所示:

>>> myfunc.cache
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'cache'
>>> myfunc(10)
12
>>> myfunc.cache
{10: 12}
def white\u space\u finder(text,new\u line\u finder=re.compile(r“\s*\n\s*”),
白空间查找器=重新编译(r“\s\s+”):
text=new\u line\u finder.sub(“\n”,text)
text=white\u space\u finder.sub(“,text)
返回文本
由于默认函数参数是经过计算的,因此它们只会被加载一次,并且不会出现在模块名称空间中。如果您真的需要,它们还可以让您灵活地替换调用代码时使用的代码。缺点是有些人可能认为它污染了函数签名。

我想尝试计时,但我不知道如何正确使用
timeit
。您应该会看到与全局版本类似的结果

不过,马库斯对你文章的评论是正确的;有时将变量放在模块级是很好的。如果你不想让他们很容易看到其他模块,那么,考虑用下划线准备名字;如果您从模块导入中执行
*
,它将不会导入以下划线开头的名称(但是,如果您按名称向他们询问,您仍然可以获取这些名称)


永远记住;“在Python中实现这一点的最佳方法是什么”的结尾几乎总是“什么使代码最具可读性?”Python的创建首先也是最重要的,是为了易于阅读,所以做您认为最具可读性的事情。

另一种方法是将通用功能分组到一个类中:

class ReUtils(object):
    new_line_finder = re.compile(r"\s*\n\s*")
    white_space_finder = re.compile(r"\s\s+")

    @classmethod
    def white_space_cleaner(cls, text):
        text = cls.new_line_finder.sub("\n", text)
        text = cls.white_space_finder.sub(" ", text)
        return text

if __name__ == '__main__':
   print ReUtils.white_space_cleaner("the text")

它已经分组在一个模块中,但根据代码的其余部分,一个类也可能是合适的。

在这种特殊情况下,我认为这无关紧要。检查:

正如您在答案和源代码中所看到的:

re
模块的实现具有正则表达式本身的缓存。因此,您看到的小速度可能是因为您避免了对缓存的查找

现在,与问题一样,有时候做类似的事情非常重要,比如,再次构建一个内部缓存,该缓存保持与函数同名

def heavy_processing(arg):
    return arg + 2

def myfunc(arg1):
    # Assign attribute to function if first call
    if not hasattr(myfunc, 'cache'):
        myfunc.cache = {}

    # Perform lookup in internal cache
    if arg1 in myfunc.cache:
        return myfunc.cache[arg1]

    # Very heavy and expensive processing with arg1
    result = heavy_processing(arg1)
    myfunc.cache[arg1] = result
    return result
这是这样执行的:

>>> myfunc.cache
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'cache'
>>> myfunc(10)
12
>>> myfunc.cache
{10: 12}
>>myfunc.cache
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:“函数”对象没有属性“缓存”
>>>myfunc(10)
12
>>>myfunc.cache
{10: 12}

您所做的一切都很好。有时,全局变量是最佳解决方案,尤其是当它是一个无法编辑的静态值时。当变量的值从其他地方更改而您不希望它更改时,问题通常会出现。对于像编译的正则表达式值这样的只读变量,这不应该是个问题。这在本例中不太起作用,因为根据是否找到换行符,您正在子绑定不同的值。根据你的想法,我想你可能会使用字典,但那肯定没有你给出的例子那么简洁易读。哦,对了,我错过了。我可以解决这个问题,并提供一个更具可读性的替代方案(尽管这个解决方案并不太糟糕)。我检查了这个答案,因为它实现了加速,并且可以轻松地扩展到更复杂的示例,但是chepner的答案也达到了速度,在你对几种不同的模式做完全相同的事情的情况下,答案可能更清晰,AlexVan Liew的答案也有效,在模式数量较少或需要灵活性的情况下,答案可能更清晰