网站首页 > 知识剖析 正文
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__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True
旧式类不继承于 object。旧式实例总是通过内置的 instance 类型来实现。
# old-style class
class Old:
pass
# old-style instance
old = Old()
old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
在 Python 3 中,旧式的类被删除了。
Python 3 中的新式类隐式地继承自 object,因此不再需要指定 MyClass(object)。
class MyClass:
pass
my_inst = MyClass()
type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
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
猜你喜欢
- 2025-01-13 「Tomcat优化篇」如何让你的Tomcat性能更加优越 一遍就懂
- 2025-01-13 C# 13 和 .NET 9 全知道 :14 使用 Blazor 构建交互式 Web 组件 (2)
- 2025-01-13 零基础学习HTML之CSS篇样式优先权和字体样式与段落样式
- 2025-01-13 CSS display 属性用法详解
- 2025-01-13 JavaScript三种动态改变样式属性
- 2025-01-13 Vue3 样式绑定: 内联样式与Class属性的数组语法
- 2025-01-13 Selenium根据class属性进行不同方式的定位
- 2025-01-13 JS中的类?class语法糖??? 两分钟秒了
- 2025-01-13 一文读懂Java的Class文件结构
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)