3: 基本继承
Python 中的继承基于 Java、C++ 等其他面向对象语言中使用的类似思想。一个新类可以从一个现有类派生出来,如下所示。
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
BaseClass 是已经存在的(父)类,而 DerivedClass 是继承(或子类化)了 BaseClass 属性的新(子)类。注:从 Python 2.2 开始,所有类都隐式地继承自对象类,对象类是所有内置类型的基类。
我们在下面的示例中定义了一个父类 Rectangle,它隐式地继承自 object:
class Rectangle():
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def perimeter(self):
return 2 * (self.w + self.h)
矩形类可以作为定义正方形类的基类,因为正方形是矩形的特例。
class Square(Rectangle):
def __init__(self, s):
# call parent constructor, w and h are both s
super(Square, self).__init__(s, s)
self.s = s
Square 类将自动继承 Rectangle 类以及对象类的所有属性。super()用于调用 Rectangle 类的 __init__() 方法,本质上是调用基类的任何重载方法。
注意:在 Python 3 中,super() 不需要参数。
派生类对象可以访问和修改基类的属性:
r.area()
# Output: 12
r.perimeter()
# Output: 14
s.area()
# Output: 4
s.perimeter()
# Output: 8
与继承相关的内置函数
issubclass(DerivedClass, BaseClass):如果 DerivedClass 是 BaseClass 的子类,则返回 True
isinstance(s, Class):如果 s 是 Class 或 Class 的任何派生类的实例,则返回 True
# subclass check
issubclass(Square, Rectangle)
# Output: True
# instantiate
r = Rectangle(3, 4)
s = Square(2)
isinstance(r, Rectangle)
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square
isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True
4: 猴子补丁
在这种情况下,"猴子补丁" 指的是在类被定义后为其添加一个新变量或方法。例如,我们把类 A 定义为
class A(object):
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other.num)
但现在我们想在代码后面添加另一个函数。假设这个函数如下
def get_num(self):
return self.num
但我们如何将其添加为 A 中的方法呢?很简单,我们只需在 A 中加入赋值语句即可。
A.get_num = get_num
为什么会这样呢?因为函数和其他对象一样是对象,而方法是属于类的函数。
函数 get_num 应可用于所有现有的(已创建的)以及 A 的新实例。
类(或其子类)的所有实例自动使用这些新增功能。例如
foo = A(42)
A.get_num = get_num
bar = A(6);
foo.get_num() # 42
bar.get_num() # 6
需要注意的是,与其他一些语言不同的是,这种方法不适用于某些内置类型,而且也不被认为是好的风格。
5: 新样式类 vs. 旧样式类
Python 2.2 引入了新样式类来统一类和类型。它们继承自顶层的 object 类型。新样式类是用户定义的类型,与内置类型非常相似。
# new-style class
class New(object):
pass
# new-style instance
new = New()
new.__class__
#
type(new)
#
issubclass(New, object)
# True
旧式类不继承于 object。旧式实例总是通过内置的 instance 类型来实现。
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
#
type(old)
#
issubclass(Old, object)
# False
在 Python 3 中,旧式的类被删除了。
Python 3 中的新式类隐式地继承自 object,因此不再需要指定 MyClass(object)。
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
#
my_inst.__class__
#
issubclass(MyClass, object)
# True
6: 类方法:替代初始化器
类方法是构建类实例的另一种方法。举个例子来说明。
假设我们有一个相对简单的 Person 类:
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
def greet(self):
print("Hello, my name is " + self.full_name + ".")
如果能提供一种方法来构建该类的实例,指定全名而不是分别指定姓和名,可能会很方便。一种方法是让 last_name 成为一个可选参数,并假设如果没有给出该参数,我们就传递了全名:
class Person(object):
def __init__(self, first_name, age, last_name=None):
if last_name is None:
self.first_name, self.last_name = first_name.split(" ", 2)
else:
self.first_name = first_name
self.last_name = last_name
self.full_name = self.first_name + " " + self.last_name
self.age = age
def greet(self):
print("Hello, my name is " + self.full_name + ".")
不过,这段代码有两个主要问题:
- 参数 first_name 和 last_name 现在会引起误解,因为您可以为 first_name 输入全名。此外,如果有更多的情况和/或更多的参数具有这种灵活性,if/elif/else 分支就会很快变得很烦人。
- 虽然不那么重要,但仍值得指出:如果 last_name 是 None,但 first_name 没有通过空格分割成两个或多个内容,该怎么办?我们又多了一层输入验证和/或异常处理...
输入类方法。我们将创建一个名为 from_full_name 的单独初始化器,并使用(内置的) classmethod 装饰器对其进行装饰,而不是使用单一的初始化器。
class Person(object):
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
self.full_name = first_name + " " + last_name
@classmethod
def from_full_name(cls, name, age):
if " " not in name:
raise ValueError
first_name, last_name = name.split(" ", 2)
return cls(first_name, last_name, age)
def greet(self):
print("Hello, my name is " + self.full_name + ".")
注意 from_full_name 的第一个参数是 cls 而不是 self。类方法应用于整个类,而不是给定类的实例(self 通常表示实例)。因此,如果 cls 是我们的 Person 类,那么 from_full_name 类方法的返回值就是 Person(first_name, last_name, age),它会使用 Person 的 __init__ 来创建 Person 类的实例。特别是,如果我们要创建 Person 的子类 Employee,那么 from_full_name 也会在 Employee 类中工作。
为了证明它的工作原理符合预期,让我们在不使用 __init__ 分支的情况下,用多种方法创建 Person 的实例:
>>> bob = Person("Bob", "Bobberson", 42)
>>> alice = Person.from_full_name("Alice Henderson", 31)
>>> bob.greet()
Hello, my name is Bob Bobberson.
>>> alice.greet()
Hello, my name is Alice Henderson.
其他参考资料:
- Python @classmethod 和 @staticmethod 适合初学者吗?「链接」
- https://docs.python.org/3.5/library/functions.html#classmethod 2. Built-in Functions Python 3.5.9 documentation