在python中定义函数内部的本地类是一个坏主意吗?

时间:2014-03-19 06:28:31

标签: python scope

不久前我正在研究的一个功能是这样的结构:

def function():
    class Inner:
        #class stuff
    #function stuff

Inner仅在function内使用和需要,并且在函数末尾也不会返回。定义本地课程是一个坏主意吗?我已经听过很多关于它如何对性能有害的事情,因为python必须在每次运行函数时重新编译类,并且由于性能是我打算用这个函数实现的,我有点担心这样做

3 个答案:

答案 0 :(得分:3)

不是那么重新编译,而是重新评估。一个简单的测试是制作这样的模块:

class Root(object):
    print("root")


def make_inner():
    class Inner(object):
        print("inner")

    return Inner()

print("importing")

然后尝试运行此

>>> import inner
root
importing
>>> import inner
>>> inner.make_inner()
inner
<inner.Inner object at 0x12efad0>
>>> inner.make_inner()
inner
<inner.Inner object at 0x12efb50>
>>>
>>> reload(inner)
root
importing
<module 'inner' from 'inner.pyc'>

基本上,您可以看到每次调用make_inner时都会执行类定义。如果在循环中调用该特定函数,这可能是一个问题,例如,它几乎就像重新加载类定义一样,但实际上并非如此。

答案 1 :(得分:3)

除了效率低下之外,每个Inner类都是一个全新的对象,因此单独的实例永远不会是同一个类,这在某种程度上违背了类的含义:

In [4]: def factory():
   ...:     class Inner(object):
   ...:         pass
   ...:     return Inner
   ...:

In [5]: i1 = factory()()

In [6]: i2 = factory()()

In [7]: i1.__class__
Out[7]: __main__.Inner

In [8]: i2.__class__
Out[8]: __main__.Inner

In [9]: i1.__class__ is i2.__class__
Out[9]: False

In [10]: isinstance(i1, i2.__class__)
Out[10]: False

如果每个实例完全独立,这不是问题,但需要注意的事项。

答案 2 :(得分:1)

在你的案例中定义一个本地类似乎没用。如果 定义本地类时有一些缺点:

  • 可读性:要么类很简单,要么函数不可避免地长得很长,而且它的逻辑在类声明中丢失了。如果你在某处有一些嵌套循环,你还有一个额外的缩进级别可能会损害可读性
  • 性能:每次调用函数时都会重新构造类。这通常不会花费大量时间,但会花费一些。如果您运行的功能很快,则此成本可能很高。

定义本地类也有一些优点:

  • 地点:你通常很确定这个课程不会在你的功能之外使用,你没有预期的方式
  • 性能:查找局部变量比查找全局变量要快得多。如果创建大量实例,则可能会提高使用全局类的性能。但是,通过默认参数/局部变量来抵消这种优势真的很容易。

我的建议是简单地全局定义类,如果它应该是私有的,则使用以下划线开头的名称,如_MyClass,因为这是用于表示私有项的约定。


了解绩效变化的一些时间:

In [1]: class _Out(object):
   ...:     def test(self):
   ...:         for _ in range(10):
   ...:             pass
   ...:         

In [2]: def function_out(n):
   ...:     for _ in range(n):
   ...:         _Out().test()
   ...:         

In [3]: def function_in(n):
   ...:     class Inner(object):
   ...:         def test(self):
   ...:             for _ in range(10):
   ...:                 pass
   ...:     for _ in range(n):
   ...:         Inner().test()
   ...:         

In [4]: def function_mixed(n, cls=_Out):
   ...:     # use of default to access the global class via local variable
   ...:     for _ in range(n):
   ...:         cls().test()
   ...:         

In [5]: %timeit function_out(1000)
1000 loops, best of 3: 602 us per loop

In [6]: %timeit function_in(1000)
1000 loops, best of 3: 621 us per loop

In [7]: %timeit function_mixed(1000)
1000 loops, best of 3: 590 us per loop

In [8]: %timeit function_out(100000)
10 loops, best of 3: 59.9 ms per loop

In [9]: %timeit function_in(100000)
10 loops, best of 3: 60.2 ms per loop

In [10]: %timeit function_mixed(100000)
10 loops, best of 3: 58.4 ms per loop

In [11]: %timeit function_out(10)
100000 loops, best of 3: 6.52 us per loop

In [12]: %timeit function_in(10)
10000 loops, best of 3: 57.8 us per loop

In [13]: %timeit function_mixed(10)
100000 loops, best of 3: 6.33 us per loop

请注意,大量迭代function_infunction_out大约在同一时间运行,而迭代次数较少function_in大约慢10倍。