最近我在Stackoverflow上看到有几个人做这类事情:
class A:
foo = 1
class B:
def blah(self):
pass
换句话说,它们有嵌套类。这是可行的(虽然 Python 初学者似乎会遇到问题,因为它的行为不像他们想象中的那样),但我想不出在任何语言中这样做的理由,尤其是在 Python 中。有这样的用例吗?人们为什么要这样做?搜索发现,在 C++ 中这种做法似乎相当普遍,是否有充分的理由呢?
它允许您控制嵌套类的访问-例如,它经常用于实现详细信息类。在C ++中,它还具有在何时解析各种内容以及无需先声明即可访问的优势。
std::map<K,V>::rb_node
之类的内容。 - Charles SalviaList
内部的Node
类并不是一个独立的类,而是列表的实现细节。同时,你可以在Tree
或Graph
内部有一个Node
内部类。无论编译器/解释器是否允许你访问该类都是不同的事情。编程是关于编写计算机和其他程序员能读懂的规范,List.Node
比将ListNode
作为第一级类更加明确,因为它表明Node
是List
内部的类。Python让你能够使用函数(包括lambda表达式)做很多事情,而在C++03或Java中,你需要用类来实现(尽管Java有匿名内部类,所以嵌套类并不总是像你的示例那样)。监听器、访问者等都需要用到类。列表推导式大致上可以看作是一种访问者:
Python:
(foo(x) if x.f == target else bar(x) for x in bazes)
C++:
struct FooBar {
Sommat operator()(const Baz &x) const {
return (x.f == val) ? foo(x) : bar(x);
}
FooBar(int val) : val(val) {}
int val;
};
vector<Sommat> v(bazes.size());
std::transform(bazes.begin(), bazes.end(), v.begin(), FooBar(target));
class list
{
public:
class iterator
{
// implementation code
};
class const_iterator
{
// implementation code
};
};
def call_count(func):
class Counter(object):
def __init__(self):
self.counter = 0
def __repr__(self):
return str(func)
def __call__(self, *args, **kw):
self.counter += 1
return func(*args, **kw)
return Counter()
并在控制台上使用它:
>>> @call_count
... def noop(): pass
...
>>> noop()
>>> noop()
>>> noop.counter
2
>>> noop
<function noop at 0x7fc251b0b578>
嵌套类定义是为了创建新的命名空间和/或更好地组织代码。例如,在Java中,可以通过使用静态嵌套类来实现这一点,并且官方文档建议使用此方法来创建更易读和可维护的代码,并将类逻辑地分组在一起。然而,《Python之禅》建议您尽量减少嵌套代码块,因此不鼓励这种做法。
import this
在Python中,您更经常看到类被分组在模块中。
将一个类放在另一个类中作为其接口的一部分(或实例的接口)。首先,实现可以使用此接口来帮助子类化,例如想象一个嵌套类HTML.Node
,您可以在HTML
的子类中覆盖它以改变用于创建新节点实例的类。其次,虽然这对于类/实例用户可能有用,但除非您处于下面描述的第三种情况,否则这并不那么有用。
至少在Python中,您不需要嵌套定义来实现上述任何一种情况,这可能非常罕见。相反,您可能会看到Node
在类之外定义,然后在类定义中使用node_factory = Node
(或专门用于创建节点的方法)。
嵌套对象的命名空间,或为不同的对象组创建不同的上下文。在Java中,非静态嵌套类(称为内部类)绑定到外部类的实例。这非常有用,因为它允许您拥有内部类的实例,这些实例位于不同的外部命名空间中。
对于Python,请考虑decimal
模块。您可以创建不同的上下文,并为每个上下文定义不同的精度。每个Decimal
对象可以在创建时分配一个上下文。这通过不同的机制实现了与内部类相同的效果。如果Python支持内部类,并且Context
和Decimal
被嵌套,则会出现context.Decimal('3')
而不是Decimal('3', context=context)
。
您可以轻松地在Python中创建元类,使您可以创建生活在实例内部的嵌套类,甚至可以使其生成支持isinstance
正确的适当绑定和未绑定类代理,通过使用__subclasscheck__
和__instancecheck__
。但是,它不会为您带来任何优势(例如__init__
的附加参数)。它只会限制您可以使用它做什么,而且每次我必须使用Java中的内部类时,我都会感到非常困惑。