我刚刚意识到,我可以设置子类的一个属性,只有它的父类才使用它
In [34]: class A:
...: def __init__(self):
...: pass
...:
...: def a(self):
...: print(f'b = {self.b}')
...:
...: class B(A):
...: def __init__(self):
...: super(B, self).__init__()
...: self.b = 1
...:
In [35]: b = B()
...: b.a()
b = 1
这个实现似乎违反直觉,感觉有点不对劲,但我不太清楚它是什么
我认为下面的话更有意义
In [38]: class A:
...: def __init__(self, b):
...: self.b = b
...:
...: def a(self):
...: print(f'b = {self.b}')
...:
...: class B(A):
...: def __init__(self):
...: super(B, self).__init__(1)
...:
In [39]: b = B()
...: b.a()
b = 1
是否存在这样的用例:前者比后者更具推荐性
从概念上讲,你在做两件不同的事情。在第一种情况下,您有一个类似于抽象类的东西;换言之,一个基类,由于某些属性的定义“缺失”而不能单独实例化;可以理解,子类将实现这些属性
更惯用的方法是使用abc
模块将A
标记为抽象基类,例如:
from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
@property
@abstractmethod
def x(self):
pass
def print_me(self):
print(f'x = {self.x}')
class B(A):
@property
def x(self):
return 1
A().print_me()
然后输出为:
TypeError: Can't instantiate abstract class A with abstract methods x
另一方面,这是可行的:
B().print_me() # prints x = 1
通过这样做,您清楚地表明子类必须重写x
属性,否则print_me
函数将无法工作
转到第二种情况,您有一个具体的基类和子类,子类的作用类似于对可以创建的实例的性质的约束。在这种情况下,A
的独立实例是完全有效的;只是B
的实例提供了额外的保证,即一个特定的属性将始终是某个特定的值(或者,如果您希望您的类是可变的,则至少将其初始化为某个特定的值)