python内部类的目的是什么?

Python的内部/嵌套类混淆了我。 没有他们的东西是不可能实现的吗? 如果是这样,那是什么东西?

引自http://www.geekinterview.com/question_details/64739

内部阶层的优点:

  • 类的逻辑分组 :如果一个类只对另外一个类有用,那么把它embedded到这个类中是合乎逻辑的,并把它们放在一起。 嵌套这样的“帮助类”使得它们的包更加简化。
  • 增加封装 :考虑两个顶级类A和B,其中B需要访问A的成员,否则将被声明为私有。 通过隐藏class级AA的成员可以被宣布为私人,B可以访问他们。 此外,B本身可以隐藏起来,从外面的世界。
  • 更易读,可维护的代码 :在顶层类中嵌套小类可以使代码更接近于使用的地方。

主要优势是组织。 任何可以用内部类来完成的事情都可以在没有它们的情况下完成。

没有他们的东西是不可能实现的吗?

不,他们完全等同于通常在顶层定义类,然后将引用复制到外部类。

我不认为有任何特殊原因嵌套的类是“允许的”,除非明确地“禁止”它们没有特别的意义。

如果你正在寻找一个在外部/所有者对象的生命周期中存在的类,并且总是有一个外部类的实例的引用 – 像Java那样的内部类,那么Python的嵌套类就不是那样了。 但是你可以破解这样的事情:

import weakref, new class innerclass(object): """Descriptor for making inner classes. Adds a property 'owner' to the inner class, pointing to the outer owner instance. """ # Use a weakref dict to memoise previous results so that # instance.Inner() always returns the same inner classobj. # def __init__(self, inner): self.inner= inner self.instances= weakref.WeakKeyDictionary() # Not thread-safe - consider adding a lock. # def __get__(self, instance, _): if instance is None: return self.inner if instance not in self.instances: self.instances[instance]= new.classobj( self.inner.__name__, (self.inner,), {'owner': instance} ) return self.instances[instance] # Using an inner class # class Outer(object): @innerclass class Inner(object): def __repr__(self): return '<%s.%s inner object of %r>' % ( self.owner.__class__.__name__, self.__class__.__name__, self.owner ) >>> o1= Outer() >>> o2= Outer() >>> i1= o1.Inner() >>> i1 <Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>> >>> isinstance(i1, Outer.Inner) True >>> isinstance(i1, o1.Inner) True >>> isinstance(i1, o2.Inner) False 

(这里使用的是类装饰器,在Python 2.6和3.0中是新的,否则你必须在类定义之后说“Inner = innerclass(Inner)”)。

有什么东西你需要包装你的头,才能够明白这一点。 在大多数语言中,类定义是编译器的指令。 也就是说,该类是在程序运行之前创build的。 在python中,所有的语句都是可执行的。 这意味着这个声明:

 class foo(object): pass 

是一个在运行时就像这样执行的语句:

 x = y + z 

这意味着不仅可以在其他类中创build类,还可以在任何地方创build类。 考虑这个代码:

 def foo(): class bar(object): ... z = bar() 

因此,“内部阶级”的概念并不是真正的语言结构。 这是一个程序员构造。 Guido有一个很好的总结,说明这是如何产生的。 但从本质上讲,基本思想是简化语言的语法。

在课堂上嵌套课程:

  • 嵌套的类膨胀了类的定义,使得难以看到发生了什么事情。

  • 嵌套类可以创build耦合,使testing更加困难。

  • 在Python中,不像Java那样,可以在一个文件/模块中放入多个类,所以类仍然接近顶级类,甚至可以用“_”作为前缀,以表示其他人不应该是使用它。

嵌套类可以certificate有用的地方在function之内

 def some_func(a, b, c): class SomeClass(a): def some_method(self): return b SomeClass.__doc__ = c return SomeClass 

该类捕获函数中的值,允许您在C ++中dynamic创build一个类似模板元编程的类

我理解反对嵌套类的观点,但是在某些场合下有使用它们的情况。 想象一下,我正在创build一个双链表类,我需要创build一个节点类来维护节点。 我有两个select,在DoublyLinkedList类中创buildNode类,或者在DoublyLinkedList类之外创buildNode类。 在这种情况下,我更喜欢第一个select,因为Node类只在DoublyLinkedList类中有意义。 虽然没有隐藏/封装的好处,但是可以说Node类是DoublyLinkedList类的一部分。

我已经使用Python的内部类在unit testing函数(即在def test_something():内创build故意错误的子类,以便接近100%的testing覆盖率(例如通过重写某些方法来testing非常罕见的触发日志语句)。

回想起来,它是类似于埃德的回答https://stackoverflow.com/a/722036/1101109

这样的内部类应该超出范围,并且一旦所有对它们的引用都被删除,就可以进行垃圾回收了。 例如,采取以下inner.py文件:

 class A(object): pass def scope(): class Buggy(A): """Do tests or something""" assert isinstance(Buggy(), A) 

在OSX Python 2.7.6下我得到了以下好奇的结果:

 >>> from inner import A, scope >>> A.__subclasses__() [] >>> scope() >>> A.__subclasses__() [<class 'inner.Buggy'>] >>> del A, scope >>> from inner import A >>> A.__subclasses__() [<class 'inner.Buggy'>] >>> del A >>> import gc >>> gc.collect() 0 >>> gc.collect() # Yes I needed to call the gc twice, seems reproducible 3 >>> from inner import A >>> A.__subclasses__() [] 

提示 – 不要继续尝试使用Django模型,这似乎保留其他(caching?)引用我的越野类。

所以一般情况下,我不会推荐使用内部类来实现这种目的,除非你真的重视100%的testing覆盖率,并且不能使用其他的方法。 虽然我觉得很高兴知道如果使用__subclasses__() ,它有时会被内部类污染。 无论哪种方式,如果你追随这一点,我认为我们现在对Python很深入,私人的dunderscores和所有。

我使用的主要用例是防止小模块的扩散, 防止不需要单独模块时的命名空间污染。 如果我正在扩展现有的类,但是现有的类必须引用另一个应该始终与之相关联的子类。 例如,我可能有一个utils.py模块,里面有许多helper类,它们不一定耦合在一起,但我想强化一些 helper类的耦合。 例如,当我实现https://stackoverflow.com/a/8274307/2718295

utils.py

 import json, decimal class Helper1(object): pass class Helper2(object): pass # Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats class DecimalJSONEncoder(json.JSONEncoder): class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched def __init__(self, obj): self._obj = obj def __repr__(self): return '{:f}'.format(self._obj) def default(self, obj): # override JSONEncoder.default if isinstance(obj, decimal.Decimal): return self._repr_decimal(obj) # else super(self.__class__, self).default(obj) # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

那么我们可以:

 >>> from utils import DecimalJSONEncoder >>> import json, decimal >>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), ... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder) {"key2": "key2_value", "key_1": 1.12345678901234} 

当然,我们json.JSONEnocderinheritancejson.JSONEnocder ,只是重写default():

 import decimal, json class Helper1(object): pass def json_encoder_decimal(obj): class _repr_decimal(float): ... if isinstance(obj, decimal.Decimal): return _repr_decimal(obj) return json.JSONEncoder(obj) >>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder) '{"key1": 1.12345678901234}' 

但是有时候只是为了约定,你想让utils由可扩展的类组成。

这里有另一个用例:我想在我的OuterClass中使用mutables的工厂,而不必调用copy

 class OuterClass(object): class DTemplate(dict): def __init__(self): self.update({'key1': [1,2,3], 'key2': {'subkey': [4,5,6]}) def __init__(self): self.outerclass_dict = { 'outerkey1': self.DTemplate(), 'outerkey2': self.DTemplate()} obj = OuterClass() obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4) assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6] 

我更喜欢使用@staticmethod装饰器的这种模式,否则它将用于工厂function。