方法只能从 python 中的 class 个后代访问
Method accessible only from class descendants in python
假设我有以下两个 类
class A:
def own_method(self):
pass
def descendant_method(self):
pass
class B(A):
pass
并且我希望 descendant_method
可以从 B
的实例调用,而不是 A
的实例,并且 own_method
可以从任何地方调用。
我想了几个办法,都不满意:
- 检查一些字段并手动加注
NotImplementedError
:
class A:
def __init__(self):
self.some_field = None
def own_method(self):
pass
def descendant_method(self):
if self.some_field is None:
raise NotImplementedError
class B(A):
def __init__(self):
super(B, self).__init__()
self.some_field = 'B'
pass
但这是在修改方法的运行时行为,我不想这样做
- 使用混入:
class A:
def own_method(self):
pass
class AA:
def descendant_method(self):
pass
class B(AA, A):
pass
这很好,只要 descendant_method
不使用 A
的太多内容,否则我们将不得不继承 AA(A)
,这违背了整点
- 在
A
中使方法私有并在元类中重新定义它:
class A:
def own_method(self):
pass
def __descendant_method(self):
pass
class AMeta(type):
def __new__(mcs, name, parents, dct):
par = parents[0]
desc_method_private_name = '_{}__descendant_method'.format(par.__name__)
if desc_method_private_name in par.__dict__:
dct['descendant_method'] = par.__dict__[desc_method_private_name]
return super(AMeta, mcs).__new__(mcs, name, parents, dct)
class B(A, metaclass=AMeta):
def __init__(self):
super(B, self).__init__()
这行得通,但显然看起来很脏,就像在 B
本身中写 self.descendant_method = self._A__descendant_method
一样。
实现此行为的正确 "pythonic" 方法是什么?
UPD:当然,将方法直接放在 B
中是可行的,但我希望 A
将有许多后代将使用此方法并且不想在每个中定义它子类。
据我所知,这可能是最Python完成您想要的事情的方式:
class A:
def own_method(self):
pass
def descendant_method(self):
raise NotImplementedError
class B(A):
def descendant_method(self):
...
另一个选项可能是:
class A:
def own_method(self):
pass
def _descendant_method(self):
pass
class B(A):
def descendant_method(self):
return self._descendant_method(self)
它们都是 Pythonic 因为它明确、可读、清晰和简洁。
- 它是明确的,因为它没有做任何不必要的魔术。
- 它是可读的,因为
一个人可以准确地说出你在做什么,你的意图是什么
乍一看。
- 很清楚,因为前导单下划线是
Python 社区中广泛使用的私有约定
(非魔术)方法——任何使用它的开发人员都应该知道
小心。
在这些方法中做出选择将取决于您打算如何处理您的用例。你的问题中的一个更具体的例子会有所帮助。
尝试使用 __class__.__name__
检查 class 名称。
class A(object):
def descendant_method(self):
if self.__class__.__name__ == A.__name__:
raise NotImplementedError
print 'From descendant'
class B(A):
pass
b = B()
b.descendant_method()
a = A()
a.descendant_method()
让 AA
继承自 A
有什么不好?它基本上是一个抽象基础 class,添加了 A
中不可用的附加功能。如果你真的不想 AA
被实例化,那么 pythonic 的答案就是不用担心它,只是记录用户不打算这样做。不过,如果您真的坚持,您可以定义 __new__
以在用户尝试实例化 AA
时抛出错误。
class A:
def f(self):
pass
class AA(A):
def g(self):
pass
def __new__(cls, *args, **kwargs):
if cls is AA:
raise TypeError("AA is not meant to be instansiated")
return super().__new__(cls)
class B(AA):
pass
另一种选择可能是将 AA
设为 Abstract Base Class。为此,您需要将至少一种方法定义为抽象方法——__init__
如果您不想说其他方法是抽象的,则可以这样做。
from abc import ABCMeta, abstractmethod
class A:
def __init__(self, val):
self.val = val
def f(self):
pass
class AA(A, metaclass=ABCMeta):
@abstractmethod
def __init__(self, val):
super().__init__(val)
def g(self):
pass
class B(AA):
def __init__(self, val):
super().__init__(val)
最后,在 A
上提供后代方法有什么不好,只是不使用它。您正在为 A
编写代码,所以不要使用该方法...您甚至可以记录该方法,它不打算由 A
直接使用,而是意味着供 classes 儿童使用。这样未来的开发人员就会知道您的意图。
假设我有以下两个 类
class A:
def own_method(self):
pass
def descendant_method(self):
pass
class B(A):
pass
并且我希望 descendant_method
可以从 B
的实例调用,而不是 A
的实例,并且 own_method
可以从任何地方调用。
我想了几个办法,都不满意:
- 检查一些字段并手动加注
NotImplementedError
:
class A:
def __init__(self):
self.some_field = None
def own_method(self):
pass
def descendant_method(self):
if self.some_field is None:
raise NotImplementedError
class B(A):
def __init__(self):
super(B, self).__init__()
self.some_field = 'B'
pass
但这是在修改方法的运行时行为,我不想这样做
- 使用混入:
class A:
def own_method(self):
pass
class AA:
def descendant_method(self):
pass
class B(AA, A):
pass
这很好,只要 descendant_method
不使用 A
的太多内容,否则我们将不得不继承 AA(A)
,这违背了整点
- 在
A
中使方法私有并在元类中重新定义它:
class A:
def own_method(self):
pass
def __descendant_method(self):
pass
class AMeta(type):
def __new__(mcs, name, parents, dct):
par = parents[0]
desc_method_private_name = '_{}__descendant_method'.format(par.__name__)
if desc_method_private_name in par.__dict__:
dct['descendant_method'] = par.__dict__[desc_method_private_name]
return super(AMeta, mcs).__new__(mcs, name, parents, dct)
class B(A, metaclass=AMeta):
def __init__(self):
super(B, self).__init__()
这行得通,但显然看起来很脏,就像在 B
本身中写 self.descendant_method = self._A__descendant_method
一样。
实现此行为的正确 "pythonic" 方法是什么?
UPD:当然,将方法直接放在 B
中是可行的,但我希望 A
将有许多后代将使用此方法并且不想在每个中定义它子类。
据我所知,这可能是最Python完成您想要的事情的方式:
class A:
def own_method(self):
pass
def descendant_method(self):
raise NotImplementedError
class B(A):
def descendant_method(self):
...
另一个选项可能是:
class A:
def own_method(self):
pass
def _descendant_method(self):
pass
class B(A):
def descendant_method(self):
return self._descendant_method(self)
它们都是 Pythonic 因为它明确、可读、清晰和简洁。
- 它是明确的,因为它没有做任何不必要的魔术。
- 它是可读的,因为 一个人可以准确地说出你在做什么,你的意图是什么 乍一看。
- 很清楚,因为前导单下划线是 Python 社区中广泛使用的私有约定 (非魔术)方法——任何使用它的开发人员都应该知道 小心。
在这些方法中做出选择将取决于您打算如何处理您的用例。你的问题中的一个更具体的例子会有所帮助。
尝试使用 __class__.__name__
检查 class 名称。
class A(object):
def descendant_method(self):
if self.__class__.__name__ == A.__name__:
raise NotImplementedError
print 'From descendant'
class B(A):
pass
b = B()
b.descendant_method()
a = A()
a.descendant_method()
让 AA
继承自 A
有什么不好?它基本上是一个抽象基础 class,添加了 A
中不可用的附加功能。如果你真的不想 AA
被实例化,那么 pythonic 的答案就是不用担心它,只是记录用户不打算这样做。不过,如果您真的坚持,您可以定义 __new__
以在用户尝试实例化 AA
时抛出错误。
class A:
def f(self):
pass
class AA(A):
def g(self):
pass
def __new__(cls, *args, **kwargs):
if cls is AA:
raise TypeError("AA is not meant to be instansiated")
return super().__new__(cls)
class B(AA):
pass
另一种选择可能是将 AA
设为 Abstract Base Class。为此,您需要将至少一种方法定义为抽象方法——__init__
如果您不想说其他方法是抽象的,则可以这样做。
from abc import ABCMeta, abstractmethod
class A:
def __init__(self, val):
self.val = val
def f(self):
pass
class AA(A, metaclass=ABCMeta):
@abstractmethod
def __init__(self, val):
super().__init__(val)
def g(self):
pass
class B(AA):
def __init__(self, val):
super().__init__(val)
最后,在 A
上提供后代方法有什么不好,只是不使用它。您正在为 A
编写代码,所以不要使用该方法...您甚至可以记录该方法,它不打算由 A
直接使用,而是意味着供 classes 儿童使用。这样未来的开发人员就会知道您的意图。