[...] 我们需要一个更好的关于继承的理论(现在仍然如此)。例如,继承和实例化(这是一种继承)混淆了语用学(例如分解代码以节省空间)和语义学(用于很多任务,例如:专业化、泛化、规范化等)。

                                                        --Alan Kay, The Early History of Smalltalk

本章是关于继承和子类化。我将假设您对这些概念有基本的了解,您可能通过阅读 Python 教程或使用其他主流面向对象语言(如 Java、C# 或 C++)的经验而了解这些概念。本章我们将重点介绍 Python 的四个特性:

  • super() 函数。
  • 子类化内置类型的陷阱。
  • 多重继承和方法解析顺序。
  • 混合类

多重继承是一个类同时拥有多个基类的能力。C++ 支持这个特性; Java 和 C# 则不支持。许多人认为多重继承是得不偿失的。在早期 C++ 代码库中被认为滥用之后,这也可能坚定了Java摒除多重继承的决心。

本章为从未使用过的人介绍了多重继承,并提供了一些指导,在必须使用继承时如何应对单继承或多重继承。

到 2021 年,由于超类和子类之间的紧耦合,因此普遍反对过度使用继承——不仅仅是多重继承。紧耦合意味着对程序某一部分的更改可能会对其他部分产生意想不到且很深的影响,使系统变得脆弱且难以理解。

然而,我们必须维护设计有复杂类层次结构的现有系统,或者使用强制我们使用继承的框架——有时甚至是多重继承。

我将通过标准库、Django Web 框架和 Tkinter GUI 工具包来说明多重继承的实际应用。

本章的新内容

没有与本章主题相关的 Python 新功能,但我根据第二版技术审阅者的反馈对其进行了大量编辑,尤其是 Leonardo Rochael 和 Caleb Hattingh。

我写了一个新的开头部分,重点介绍了 super() 内置函数,并更改了“多重继承和方法解析顺序”中的示例,以更深入地探索 super() 如何支持协作多重继承。

“混合类型”也是新的,而“现实世界中的多重继承”经过重组,涵盖了标准库中更简单的混合示例,在复杂的 Django 和复杂的 Tkinter 层次结构之前。

正如章节标题所暗示的,继承的注意事项一直是本章的主题之一。但是越来越多的开发人员认为继承就是问题,以至于我在“章节总结”和“进一步阅读”的末尾添加了几段关于避免继承的段落。

我们将从神秘的 super() 函数的概述开始。

super() 函数

使用 super() 内置函数并保持一致性对于可维护的面向对象的 Python 程序至关重要。

当子类覆盖超类的方法时,覆盖的方法通常需要调用超类的对应方法。以下是推荐的方法,来自 collections 模块文档中的示例, OrderedDict Examples and Recipes:

class LastUpdatedOrderedDict(OrderedDict):"""Store items in the order they were last updated"""def __setitem__(self, key, value):super().__setitem__(key, value)self.move_to_end(key)

为了实现这个方法,LastUpdatedOrderedDict 将 __setitem__ 覆盖为:

  1. 使用 super().__setitem__ 调用在超类上的该方法,让它插入或更新键/值对。
  2. 调用 self.move_to_end 以确保更新的键位于最后一个位置。

调用重写的 __init__ 方法以允许超类在实例中进行初始化尤为重要。

TIP:

如果您在 Java 中学习了面向对象编程,您可能还记得 Java 构造函数方法会自动调用超类的无参数构造函数。 Python 不会这样做。你必须习惯按照这个模式编写__init__:

    def __init__(self, a, b) :super().__init__(a, b)...  # more initialization code

你也许见过不使用 super() 而是直接在超类上调用方法的代码,如下所示:

class NotRecommended(OrderedDict):"""This is a counter example!"""def __setitem__(self, key, value):OrderedDict.__setitem__(self, key, value)self.move_to_end(key)

这种替代方法同样适用于这种特殊情况,但由于两个原因不推荐使用。

首先,它对基类进行了硬编码。名称 OrderedDict 出现在 class 语句和 __setitem__ 中。如果将来有人更改 class 语句更改了基类或添加另一个基类,他们可能会忘记更新 __setitem__ 的主体,从而引入缺陷。

第二个原因是 super 实现了处理具有多重继承的类层次结构的逻辑。我们将在 “Multiple Inheritance and Method Resolution Order”中回到这部分。总结一下关于 super 的复习,回顾一下我们如何在 Python 2 中调用它,因为带有两个参数的旧签名揭示了:

class LastUpdatedOrderedDict(OrderedDict):"""This code works in Python 2 and Python 3"""def __setitem__(self, key, value):super(LastUpdatedOrderedDict, self).__setitem__(key, value)self.move_to_end(key)

super 的两个参数现在都是可选的。当在方法中调用 super() 时,Python 3 字节码编译器通过检查周围的上下文进行自动提供。这些参数是:

 type:

        实现所需方法的超类的搜索路径的开始。默认情况下,它是调用 super() 方法的类。

object_or_type:

        作为方法调用接收者的对象(例如实例方法调用)或类(对于类方法调用)。默认情况下,如果 在实例中调用了super(),则这个参数的值是 self 。

无论您还是编译器提供这些参数,super() 调用都会返回一个动态代理对象,该对象在type参数对应的超类中查找方法(例如示例中的 __setitem__),并将这个动态代理对象绑定到 object_or_type,这样我们就不需要显式传递接收者(self)然后调用该方法。

在 Python 3 中,您仍然可以显式向 super() 传递第一个和第二个参数。但是只有在特殊情况下才需要这样做,例如跳过部分 MRO 以进行测试、调试或解决超类中的异常行为。

现在让我们讨论对内置类型进行子类化时的注意事项。

子类化内置类型很麻烦

在 Python 的最早版本中,不能子类化内置类型(如 list 或 dict)。从 Python 2.2 开始,这样做是可能的,但有一个重要的注意事项:内置类型的代码(用 C 编写)通常不会调用被用户定义的类覆盖的方法。

在 PyPy 的文档中,关于PyPy 和 CPython 之间的差异,Subclasses of built-in types部分中对该问题进行了正确的简短描述:

       在官方途径中中,关于何时隐式调用内置类型的子类覆盖的方法,CPython 根本没有制定规则。基本上,这些方法永远不会被同一对象的其他内置方法调用。例如, dict 子类中被覆盖的 __getitem__() 方法不会被内置类型的 get() 方法调用。

示例 14-1 说明了这个问题。

例 14-1。我们覆盖的__setitem__ 方法被内置 dict 的 __init__ 和 __update__ 方法忽略

>>> class DoppelDict(dict):
...     def __setitem__(self, key, value):
...         super().__setitem__(key, [value] * 2)  1
...
>>> dd = DoppelDict(one=1)  2
>>> dd
{'one': 1}
>>> dd['two'] = 2  3
>>> dd
{'one': 1, 'two': [2, 2]}
>>> dd.update(three=3)  4
>>> dd
{'three': 3, 'one': 1, 'two': [2, 2]}
  1. DoppelDict.__setitem__ 存储时会重复存储一个包含两个值的列表(,只是为了有一个可见的效果)。它把职责委托给超类。
  2. 从 dict 继承的 __init__ 方法显然忽略了 __setitem__ 被覆盖:'one' 的值没有重复。
  3. [] 运算符调用了我们覆盖的 __setitem__ 并按预期工作:'two' 映射到重复值 [2, 2]。
  4. dict 中的updata方法也没有使用我们的 __setitem__ 版本:'three' 的值没有重复。

内置类型的这种行为违反了面向对象编程的基本规则:搜索方法应该始终从接收者的类(self)开始,即使调用发生在超类中实现的方法内部。这就是所谓的“后期绑定”,Smalltalk 的知名人士 Alan Kay 认为这是面向对象编程的一个关键特性:在任何形式的 x.method() 的调用中,要调用的方法必须在运行时确定,基于接收者的类 x是什么。这样的设计导致了我们在“标准库中 __missing__ 的使用不一致”中看到的问题。

问题不仅限于实例内的调用——self.get() 是否调用 self.__getitem__(),也会发生在内置方法调用其他类的重写方法。

示例 14-2 改编自 PyPy documentation。

例 14-2。 dict.update 绕过了 AnswerDict 的 __getitem__

>>> class AnswerDict(dict):
...     def __getitem__(self, key):  1
...         return 42
...
>>> ad = AnswerDict(a='foo')  2
>>> ad['a']  3
42
>>> d = {}
>>> d.update(ad)  4
>>> d['a']  5
'foo'
>>> d
{'a': 'foo'}
  1. 不管传入什么键,AnswerDict.__getitem__ 总是返回 42。
  2. ad 是一个以键值对 ('a', 'foo') 进行初始化的 AnswerDict。
  3. ad['a'] 返回 42,正如预期的那样
  4. d 是一个普通 dict 的实例,我们使用用 ad 更新d。
  5. dict.update 方法忽略了我们的 AnswerDict.__getitem__。

Warning:

直接对 dict 或 list 或 str 等内置类型进行子类化容易出错,因为内置类型的方法大多会忽略用户定义的覆盖的方法。不要子类化内置类型,而是继承 UserDict、UserList 和 UserString 等collections模块派生类,它们做了特殊设计,易于扩展。

如果子类化一个 collections.UserDict 而不是 dict,则示例 14-1 和 14-2 中暴露的问题都已修复。请参见示例 14-3。

例 14-3。 DoppelDict2 和 AnswerDict2 按预期工作,因为它们扩展 UserDict 而不是 dict

>>> import collections
>>>
>>> class DoppelDict2(collections.UserDict):
...     def __setitem__(self, key, value):
...         super().__setitem__(key, [value] * 2)
...
>>> dd = DoppelDict2(one=1)
>>> dd
{'one': [1, 1]}
>>> dd['two'] = 2
>>> dd
{'two': [2, 2], 'one': [1, 1]}
>>> dd.update(three=3)
>>> dd
{'two': [2, 2], 'three': [3, 3], 'one': [1, 1]}
>>>
>>> class AnswerDict2(collections.UserDict):
...     def __getitem__(self, key):
...         return 42
...
>>> ad = AnswerDict2(a='foo')
>>> ad['a']
42
>>> d = {}
>>> d.update(ad)
>>> d['a']
42
>>> d
{'a': 42}

作为测量对内置函数进行子类化所需的额外工作的实验,我将示例 3-9 中的 StrKeyDict 类重写为继承 dict 而不是 UserDict。为了使其通过相同的测试,我必须实现 __init__、get 和 update方法,因为从 dict 继承的版本拒绝与覆盖的 __missing__、__contains__ 和 __setitem__ 合作。Example 3-9 中的 UserDict 子类有 16 行,而实验 dict 子类最终有 33 行。

需要明确的是:本节涵盖的问题仅适用于内置类型的 C 语言代码中的方法委托,并且仅影响直接从这些类型派生的类。如果你子类化一个用 Python 编码的基类,比如 UserDict 或 MutableMapping,你就不会被这个问题困扰。

现在让我们关注一个多重继承带来的问题:如果一个类有两个超类,当我们调用 super().attr 时,如果两个超类都有一个同名的属性,Python 如何决定使用哪个属性?

多重继承和方法解析顺序

图 14-1。左图:leaf1.ping() 调用的方法解析顺序。右图:leaf1.pong() 调用的方法解析顺序。 

当超类实现同名方法时,任何实现多重继承的语言都需要处理潜在的命名冲突。这称为“钻石问题”,如图 14-1 和示例 14-4 所示。

class Root:  1def ping(self):print(f'{self}.ping() in Root')def pong(self):print(f'{self}.pong() in Root')def __repr__(self):cls_name = type(self).__name__return f'<instance of {cls_name}>'class A(Root):  2def ping(self):print(f'{self}.ping() in A')super().ping()def pong(self):print(f'{self}.pong() in A')super().pong()class B(Root):  3def ping(self):print(f'{self}.ping() in B')super().ping()def pong(self):print(f'{self}.pong() in B')class Leaf(A, B):  4def ping(self):print(f'{self}.ping() in Leaf')super().ping()
  1. Root 提供 ping、pong 和 __repr__ 以使输出更易于阅读。
  2. A 类中的 ping 和 pong 方法都委托给 super()。
  3. B 类中的 只有ping 方法委托给 super()。
  4. Leaf 类只实现了 ping,并委托给 super()。

现在让我们看看在 Leaf 的实例上调用 ping 和 pong 方法的效果。

例 14-5。用于在 Leaf 对象上调用 ping 和 pong 的 Doctests。

    >>> leaf1 = Leaf()  1>>> leaf1.ping()    2<instance of Leaf>.ping() in Leaf<instance of Leaf>.ping() in A<instance of Leaf>.ping() in B<instance of Leaf>.ping() in Root>>> leaf1.pong()   3<instance of Leaf>.pong() in A<instance of Leaf>.pong() in B
  1. Leaf1 是 Leaf 的一个实例
  2. 调用 Leaf1.ping() 会激活 Leaf、A、B 和 Root 中的 ping 方法,因为前三个类中的 ping 方法调用 super().ping()。
  3. 调用leaf1.pong() 通过继承激活A 中的pong,然后调用super.pong() 激活B.pong。

示例 14-5 和图 14-1 中显示的激活序列由两个因素决定:

  1. Leaf 类的方法解析顺序。
  2. 在每个方法中使用 super()。 

每个类都有一个名为 __mro__ 的属性,其中包含一个按方法解析顺序对超类的引用的元组,从当前类一直到Object类。对于 Leaf 类,这是它的 __mro__:

>>> Leaf.__mro__  # doctest:+NORMALIZE_WHITESPACE(<class 'diamond1.Leaf'>, <class 'diamond1.A'>, <class 'diamond1.B'>,<class 'diamond1.Root'>, <class 'object'>)

NOTE:

查看图 14-1,您可能认为 MRO 描述了广度优先搜索,但这对于特定的类层次结构来说只是巧合。MRO 是由一种称为 C3 的已发布算法计算的。它在 Python 中的使用在 Michele Simionato 的  The Python 2.3 Method Resolution Order. 中有详细说明。这是一篇具有挑战性的文章,但 Simionato 写道:“除非你充分利用多重继承并且类的层次结构非常复杂,否则你不需要理解 C3 算法,你可以轻松地跳过这篇论文。”

MRO 只决定调用顺序,但是否会在每个类中激活特定方法取决于每个实现是否调用 super()。

下面是使用 pong 方法的实验。 Leaf 类没有覆盖它,因此调用 Leaf1.pong() 会激活 Leaf.__mro__ 的下一个类中的实现:A 类。方法 A.pong 调用 super().pong()。 B 类是 MRO 中的下一个类,因此 B.pong 被激活。但是B.pong 没有调用 super().pong(),所以激活序列到这里结束。

MRO 不仅考虑继承图,还考虑在子类声明中的超类列出顺序。换句话说,如果在 diamond.py(示例 14-4)中将 Leaf 类声明为 Leaf(B, A),那么类 B 出现在 Leaf.__mro__ 中的 顺序在类A 之前。这会影响ping方法的激活顺序,也会导致leaf1.pong()通过继承激活B.pong,但是A.pong和Root.pong永远不会运行,因为B.pong没有调用super( )。

当一个方法调用 super() 时,它是一个协作方法。协作方法支持协作多重继承。这些术语是有意的:为了工作,Python 中的多重继承需要相关方法的协作。在 B 类中,ping 方法选择了合作,但 pong选择了 不合作。

Warning:

未进行协作的方法可能是导致细微错误的原因。大多数阅读示例 14-4 的编码人员可能会认为,当方法 A.pong 调用 super.pong() 时,最终会激活 Root.pong。但是如果 B.pong 在 Root.pong之前被激活, Root.pong就永远不会被激活。这就是为什么建议非根节点类的每个方法 m 都应该调用 super().m() 的原因。

协作方法必须有兼容的签名,因为你永远不会知道 A.ping 会在 B.ping 之前还是之后被调用:激活顺序取决于继承这

两个类的每个子类的声明中 A 和 B 的顺序。 

Python 是一种动态语言,因此 super() 与 MRO 的交互也是动态的。示例 14-6 显示了这种动态行为的惊人结果。

例 14-6。 Diamond2.py:展示 super() 动态特性的类。

from diamond import A  1class U():  2def ping(self):print(f'{self}.ping() in U')super().ping()  3class LeafUA(U, A):  4def ping(self):print(f'{self}.ping() in LeafUA')super().ping()
  1.  A 类来自 diamond.py(示例 14-4)。
  2. U 类与diamond模块中的类 A 或 Root 没有关联。
  3. super().ping() 有什么作用?答:视情况而定。继续阅读。
  4. LeafUA按照U和A的顺序进行继承。

如果创建 U 的实例并尝试调用 ping,则会出现错误:

    >>> u = U()>>> u.ping()Traceback (most recent call last):...AttributeError: 'super' object has no attribute 'ping'

super()返回的'super'对象没有'ping'属性,因为U的MRO有两个类:U和object,后者没有名为'ping'的属性。

但是,U.ping 方法并非完全没有希望。看一下这个:

    >>> leaf2 = LeafUA()>>> leaf2.ping()<instance of LeafUA>.ping() in LeafUA<instance of LeafUA>.ping() in U<instance of LeafUA>.ping() in A<instance of LeafUA>.ping() in Root>>> LeafUA.__mro__  # doctest:+NORMALIZE_WHITESPACE(<class 'diamond2.LeafUA'>, <class 'diamond2.U'>,<class 'diamond.A'>, <class 'diamond.Root'>, <class 'object'>)

 LeafUA 中的 super().ping() 调用激活了 U.ping,它也通过调用 super().ping() 进行协作,激活 A.ping,并最终激活 Root.ping。

注意 LeafUA 的基类是按照 (U, A) 的顺序。如果是顺序相反的基类(A, U),那么leaf2.ping() 将永远不会到达 U.ping,因为 A.ping 中的 super().ping() 会激活 Root.ping,并且该方法不会调用 super ()。

在实际的编程中,像 U 这样的类是一个 mixin 类:一个旨在与多重继承中的其他类一起使用的类,以提供额外的功能。我们很快就会在“混合类”中学习。

为了结束对 MRO 的讨论,图 14-2 说明了 Python 标准库中 Tkinter GUI 工具包的复杂多重继承图的一部分。

要研究这张图,请从底部的 Text 类开始。 Text 类实现了一个功能齐全的多行可编辑文本小组件。它本身提供了丰富的功能,但也继承了其他类的许多方法。左侧显示了一个简单的 UML 类图。在右侧,它标注了有显示 MRO 的箭头,如在 print_mro 便利函数的帮助下列出的:

>>> def print_mro(cls):
...     print(', '.join(c.__name__ for c in cls.__mro__))
>>> import tkinter
>>> print_mro(tkinter.Text)
Text, Widget, BaseWidget, Misc, Pack, Place, Grid, XView, YView, object

现在让我们谈谈mixin。

Mixin 类 

一个 mixin 类被设计为与至少一个其他类以多重继承一起被子类化。mixin 不应该是具体类的唯一基类,因为它不提供具体对象的所有功能,而只是添加或自定义子类或兄弟类的行为。
Note:

Mixin 类是一个约定,在 Python 和 C++ 中没有明确的语言支持。Ruby 允许显式定义和使用作为mixin的模块——可以包含方法的集合以向类添加功能。 C#、PHP 和 Rust 实现了 trait,这也是 mixin 的一种显式形式。

让我们看一个简单但方便的 mixin 类示例。

不区分大小写的映射

示例 14-8 展示了 UpperCaseMixin,该类旨在通过在添加或查找这些键时将这些键大写来提供对带有字符串键的映射的不区分大小写的访问。

import collectionsdef _upper(key):  1try:return key.upper()except AttributeError:return keyclass UpperCaseMixin:  2def __setitem__(self, key, item):super().__setitem__(_upper(key), item)def __getitem__(self, key):return super().__getitem__(_upper(key))def get(self, key, default=None):return super().get(_upper(key), default)def __contains__(self, key):return super().__contains__(_upper(key))
  1. 这个辅助函数接受任何类型的键,并尝试返回 key.upper();如果失败,它将返回未更改的key
  2. mixin 实现了四种基本的映射方法,这些方法都调用了 super(),如果可以就将键大写。

由于 UpperCaseMixin 的每个方法都调用 了super(),因此这个 mixin 依赖于实现或继承具有相同签名的方法的兄弟类。为了能够让自己起作用,mixin 通常需要出现在继承它的子类的 MRO 中的其他类之前。实际上,这意味着 mixin 必须首先出现在类声明的基类元组中。这里有两个例子:

例 14-9。 uppermixin.py:两个使用 UpperCaseMixin 的类。

class UpperDict(UpperCaseMixin, collections.UserDict):  1passclass UpperCounter(UpperCaseMixin, collections.Counter):  2"""Specialized 'Counter' that uppercases string keys"""  3
  1. UpperDict 不需要自己的实现,但 UpperCaseMixin 必须是第一个基类,否则将先调用 UserDict 中的方法。
  2. UpperCaseMixin 也适用于 Counter。
  3. 最好提供一个 docstring 来满足 class 语句语法中对主体的需求,而不是 pass。

这里有一些来自 uppermixin.py 的 doctest,对于 UpperDict:

 >>> d = UpperDict([('a', 'letter A'), (2, 'digit two')])>>> list(d.keys())['A', 2]>>> d['b'] = 'letter B'>>> 'b' in dTrue>>> d['a'], d.get('B')('letter A', 'letter B')>>> list(d.keys())['A', 2, 'B']

以及 UpperCounter 的快速演示:

    >>> c = UpperCounter('BaNanA')>>> c.most_common()[('A', 3), ('N', 2), ('B', 1)]

UpperDict 和 UpperCounter 看起来几乎很神奇,但我必须仔细研究 UserDict 和 Counter 的代码才能使 UpperCaseMixin 与它们一起工作。

例如,我的第一个 UpperCaseMixin 版本没有提供 get 方法。该版本适用于 UserDict 但不适用于 Counter。 UserDict 类从 collections.abc.Mapping 继承了 get,而那个 get 调用了我实现的 __getitem__。但是当 UpperCounter 在 __init__ 上进行初始化时,键就不是大写的。这是因为 Counter.__init__ 使用 Counter.update,而后者又依赖于从 dict 继承的 get 方法。但是dict类中的get方法并没有调用__getitem__。这是“标准库中 __missing__ 的使用不一致”中讨论的问题的核心。它也清楚地提醒我们利用继承的程序的脆弱性和令人费解的本质,即使是小规模的。

下一节将介绍多重继承的几个示例,通常使用 mixin 类的特性。

多重继承的真实应用

在 Design Patterns book 中,几乎所有的代码都是用 C++ 编写的,但多重继承的唯一例子是 适配器模式。在 Python 中,多重继承也不是经常出现,但我将在本节中讲述一些重要的例子。

ABC 也是 Mixin

在 Python 标准库中,最常使用多重继承用法是 collections.abc 包。这没有争议:毕竟,即使 Java 也支持接口的多重继承,而且 ABC 是接口声明,可以选择提供具体的方法实现。

Python 的 collections.abc 官方文档使用术语 mixin 方法来表示在许多集合 ABC 中实现的非抽象方法。提供 mixin 方法的 ABC 扮演两个角色:它们提供了接口定义同时也是 mixin 类。例如,collections.UserDict 的实现依赖于 collections.abc.MutableMapping 提供的几个 mixin 方法。

ThreadingMixin 和 ForkingMixin

http.server 包提供了 HTTPServer 和 ThreadingHTTPServer 类。后者是在 Python 3.7 中添加的。它的文档是这样说的:

class http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)

此类与 HTTPServer 是相同的,但是使用线程来处理请求,这利用了ThreadingMixIn。这对于处理 Web 浏览器预打开套接字很有用,如果是HTTPServer的话,它将会无限期等待。

这是 Python 3.10 中 ThreadingHTTPServer 类的完整源代码:

class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):daemon_threads = True

socketserver.ThreadingMixIn 的源代码有 38 行,包括注释和文档字符串。以下是其实现的摘要:

例 14-10。 Python 3.10 中 Lib/socketserver.py 的一部分。

class ThreadingMixIn:"""Mix-in class to handle each request in a new thread."""# 8 lines omitted in book listingdef process_request_thread(self, request, client_address):  1... # 6 lines omitted in book listingdef process_request(self, request, client_address):  2... # 8 lines omitted in book listingdef server_close(self):  3super().server_close()self._threads.join()
  1. process_request_thread 没有调用 super() 因为它是一个新方法,而不是覆盖基类的方法。它的实现调用了 HTTPServer 提供或继承的三个实例方法。
  2. 这会覆盖 HTTPServer 从 socketserver.BaseServer 继承的 process_request 方法,启动一个线程并将实际工作委托给在该线程中运行的 process_request_thread。
  3. server_close 调用 super().server_close() 并停止接受请求,然后等待 process_request 启动的线程完成它们的工作。

ThreadingMixIn 出现在 ForkingMixin 旁边的 socketserver 模块文档中。后者旨在支持基于 os.fork() 的并发服务器,os.fork() 是一种用于启动子进程的 API,可在符合 POSIX 的类 Unix 系统中使用。

Django 通用视图Mixin

Note:

您无需了解 Django 即可阅读本节内容。我使用框架的一小部分作为多重继承的实际示例,假设您有使用任何语言或框架进行服务器端 Web 开发的一些经验,我将尝试提供所有必要的背景知识。

在 Django 中,视图是一个可调用对象,它接受一个request参数——一个表示 HTTP 请求的对象——并返回一个表示 HTTP 响应的对象。不同的响应是我们在这次讨论中所感兴趣的。它们可以像没有内容正文的重定向响应一样简单,也可以像在线商店中的目录页面那么复杂,从 HTML 模板呈现并列出多个带有购买按钮和详细信息页面链接的商品。

最初,Django 提供了一组称为通用视图的函数,用于实现一些常见用例。例如,许多网站需要显示包含大量元素信息的搜索 .png,列表跨越多个页面,并且每个项都有一个链接到包含有关它的详细信息的页面。在 Django 中,列表视图和详细信息视图旨在协同工作来解决下面这个问题:列表视图呈现搜索结果,详细信息视图为每个单独的项生成一个页面。

然而,最初的通用视图是函数,所以它们不可扩展。如果你需要做一些类似但不完全像通用列表视图的事情,你就不得不自己完成。

基于类的视图的概念是在 Django 1.3 中引入的,以及一组通用的视图类,这些类组织为基类、mixin 和拿来即用的具体类。在 Django 3.2 中,基类和 mixin 位于 django.views.generic 包的基本模块中,如图 14-3 所示。在图的顶部,我们看到两个职责非常不同的的类:View 和 TemplateResponseMixin。

TIP:

 Classy Class-Based Views 是学习这些类的一个很好的资源,您可以在其中轻松浏览它们,查看每个类中的所有方法(继承的、覆盖的和添加的方法)、查看图表、浏览他们的文档并跳转到他们在 GitHub 上的源代码。

View 是所有视图的基类(可以是 ABC),它提供了像 dispatch 方法这样的核心功能,它委托给“handler”方法,如 get、head、post 等,由具体的子类实现以处理不同的 HTTP 动词。View 的具体子类应该实现handler方法,那么为什么这些方法不是 View 接口的一部分呢?原因:子类可以自由实现他们想要支持的handler.TemplateView 只用于显示内容,所以它只实现了 get。如果将 HTTP POST 请求发送到 TemplateView,则继承的 View.dispatch 方法会检查是否有实现post handler,并生成 HTTP 405 Method Not Allowed 响应。

TemplateResponseMixin 提供的功能只对需要使用模板的视图感兴趣。 例如,一个 RedirectView 没有响应的内容体,所以它不需要模板,也继承这个 mixin 。TemplateResponseMixin 为 TemplateView 和其他模板渲染视图提供行为,如 ListView、DetailView 等,定义在 django.views.generic 子包中。图 14-4 描述了 django.views.generic.list 模块和base模块的一部分。

 对于 Django 用户来说,图 14-4 中最重要的类是 ListView,它是一个聚合类,没有任何代码(它的主体只是一个文档字符串)。实例化时,ListView 具有 object_list 实例属性,模板可以通过该属性进行迭代以显示页面内容,通常是返回多个对象的数据库查询结果。与生成此可迭代对象相关的所有功能都来自 MultipleObjectMixin。该 mixin 还提供了复杂的分页逻辑——在一个页面中显示部分结果并链接到更多页面。

假设您要创建一个视图,该视图不会呈现模板,但会生成 JSON 格式的对象列表。这就是 BaseListView 存在的原因。它提供了一个易于使用的扩展点,将 View 和 MultipleObjectMixin 功能结合在一起,而没有模板机制的开销。

Django 基于类的视图 API 是比 Tkinter 更好的多重继承示例。特别是,它的 mixin 类很容易理解:每个类都有明确定义的用途,并且它们都以 ...Mixin 后缀命名。

Django 用户并未普遍接受基于类的视图。许多人以有限的方式使用它们,就像一个黑盒子,但是当需要创建新的东西时,许多 Django 编码人员继续编写负责所有这些职责的整体视图函数,而不是尝试重用基本视图和mixin。

学习如何利用基于类的视图以及如何扩展它们以满足特定的应用程序需求确实需要一些时间,但我发现研究它们是值得的:他们消除了大量样板代码,更容易重用解决方案,甚至改善团队沟通。例如,通过为模板和传递给模板上下文的变量定义标准名称。基于类的视图是“在轨道上”的 Django 视图。

Tkinter 中的多重继承

Python 标准库中多重继承的一个极端例子是 Tkinter GUI 工具包。我使用了 Tkinter 小组件层次结构的一部分来说明图 14-2 中的 MRO。图 14-5 显示了 tkinter 基础包中的所有小组件类(tkinter.ttk 子包中有更多小组件)。

我写这篇文章时,Tkinter 已经 25 岁了。它不是当前最佳实践的示例。但它显示了当程序员不了解其缺点时,他们会如何使用多重继承。 当我们在下一节介绍一些好的做法时,它将作为一个反例。

考虑图 14-5 中的这些类:

  1. Toplevel:Tkinter 应用程序中顶级窗口的类。
  2. Widget:可以放置在窗口上的每个可见对象的超类。
  3. Button:一个普通的按钮小组件。
  4. Entry:单行可编辑文本字段。
  5. Text:多行可编辑文本字段。

以下是这些类的 MRO,由示例 14-7 中的 print_mro 函数显示:

>>> import tkinter
>>> print_mro(tkinter.Toplevel)
Toplevel, BaseWidget, Misc, Wm, object
>>> print_mro(tkinter.Widget)
Widget, BaseWidget, Misc, Pack, Place, Grid, object
>>> print_mro(tkinter.Button)
Button, Widget, BaseWidget, Misc, Pack, Place, Grid, object
>>> print_mro(tkinter.Entry)
Entry, Widget, BaseWidget, Misc, Pack, Place, Grid, XView, object
>>> print_mro(tkinter.Text)
Text, Widget, BaseWidget, Misc, Pack, Place, Grid, XView, YView, object

Note:

按照目前的标准,Tkinter 的类层次结构非常深。Python 标准库的几个部分很少有超过 3 或 4 级的具体类,Java 类库也是如此。然而,有趣的是,Java 类库中一些最深的层次结构正是在与 GUI 编程相关的包中:java.awt 和 javax.swing。Squeak 是 Smalltalk 的现代免费版本,包括强大且创新的 Morphic GUI 工具包,还是具有很深的类层次结构。根据我的经验,GUI 工具包是继承最有用的地方。

请注意这些类与其他类的关系:

  1. Toplevel 是唯一一个不继承自 Widget 的图形类,因为它是顶级窗口并且不像小组件那样表现——例如,它不能附加到窗口或窗体上。Toplevel 继承自 Wm,它提供宿主窗口管理器的直接访问功能,如设置窗口标题和配置其边框。
  2. Widget 直接继承了BaseWidget ,还继承了 Pack、Place 和 Grid 。最后三个类是几何管理器:它们负责在窗口或窗体内排列小组件。每个类封装了不同的布局策略和小组件放置的 API。
  3. Button 与大多数小组件一样,仅继承自 Widget,但间接继承自 Misc,后者为每个小组件提供了数十种方法。
  4. Entry 继承自 Widget 和 XView,XView支持水平滚动。
  5. Text继承自 Widget、XView 和 YView,YView 支持垂直滚动。

我们现在将讨论多重继承的一些良好实践,看看 Tkinter 是否也遵循这些实践。

处理继承

Alan Kay 在题词中所写的仍然是正确的:仍然没有可以指导程序员实践的关于继承的完整的理论。我们拥有的是经验法则、设计模式、“最佳实践”、巧妙的首字母缩略词、禁忌等。其中一些提供了有用的指导方针,但没有一个被大众普遍接受或始终适用于所有场景。

使用继承很容易创建难以理解和脆弱的设计,即使没有多重继承。因为我们没有一个全面的理论,这里有一些避免把类图做成意大利面条那样混乱的技巧。

1. 优先使用对象组合而不是类继承

本小节的标题是《设计模式》一书中的面向对象设计的第二个原则,这 是我在这里可以提供的最佳建议。一旦你习惯了继承,就很容易过度使用它。以整齐的层次结构放置对象会吸引我们的秩序感;程序员这样做只是为了好玩。

使用组合的设计技巧会带来更灵活的设计。例如,在 tkinter.Widget 类的情况下,我们可以不从所有几何管理器继承方法,而是让小组件实例可以保存对几何管理器的引用,并调用它的方法。毕竟,Widget 不应该“成为”几何管理器,而是可以通过委托使用几何管理器的服务。然后您可以设计一个新的几何管理器类,而无需影响小部件类层次结构,也无需担心名称冲突。即使是单继承,这个原则也增强了灵活性,因为继承是一种紧密耦合的形式,而高大的继承树往往是脆弱的。

组合和委托可以代替 mixin类的作用 ,但不能代替使用接口继承来定义类型的层次结构。

2. 理解为什么在每种情况下都使用继承

在处理多重继承时,直截了当地说明为什么在每个特定情况下使用继承的原因。主要原因是:

  • 接口的继承创建了一个子类型,表明了一种“is-a”关系。这最好用 ABC 来完成。
  • 继承实现,通过复用来避免代码重复。 Mixins 可以帮助解决这个问题。

在实践中,这两种用法通常是同时出现,但只要你能明确意图,那就可以做。代码复用导致的继承是一个实现细节,通常可以用组合和委托来代替。另一方面,接口继承是框架的支柱。如果可能,接口继承应该只使用 ABC 作为基类。

3. 使用 ABC 显示定义接口

在现代 Python 中,如果一个类打算定义一个接口,那么这个类应该是一个显式的 ABC 或一个 Typing.Protocol 子类。ABC 应仅子类化 abc.ABC 或其他 ABC。 ABC 的多重继承不会带来问题。

4. 使用显式 Mixins 进行代码重用

如果一个类旨在为多个不相关的子类提供可重用的方法实现,而不是表明“is-a”关系,那么它应该是一个显式的 mixin 类。从概念上讲,mixin 并没有定义新的类型。它只是绑定了需要进行重用的方法。每个 mixin 都应该提供一个特定的行为,实现数量不多且非常密切相关的方法。Mixin 应该避免持有任何内部状态——即mixin 类不应具有实例属性。

在 Python 中没有正式的方式来声明一个类是一个 mixin,所以强烈建议用 Mixin 后缀命名类。

5. 向用户提供聚合类

如果一个类主要通过从 mixin 的继承构造的。并且类不添加自己的结构或行为,那么这个类被称为聚合类。

                                                                                                        --Grady Booch et al.

例如,这是图 14-4 右下角的 Django ListView 类的完整源代码。

class ListView(MultipleObjectTemplateResponseMixin, BaseListView):"""Render some list of objects, set by `self.model` or `self.queryset`.`self.queryset` can actually be any iterable of items, not just a queryset."""

ListView 的主体是空的,但该类提供了一个有用的服务:它将一个Mixin和一个应该一起使用的基类组合在一起。

另一个例子是 tkinter.Widget,它有四个基类,没有自己的方法或属性——类体中只是一个文档字符串。多亏了 Widget 聚合类,我们可以使用所需的 mixin 创建新的小组件,而无需弄清楚它们应该以何种顺序声明以按预期工作。

请注意,聚合类不必类体完全为空,但它们通常类体是空的。

6. 只继承为子类化而设计的类

在对本章的一个评论中,技术审阅者 Leonardo Rochael 提出了这个警告:

Warning:

集成任何复杂类并覆盖其方法很容易出错,因为超类方法可能会以意想不到的方式忽略子类覆盖。尽可能避免覆盖超类的方法,或者至少只对对设计为易于扩展的类进行子类化,并且只能以它们设计扩展的方式进行子类化。

这是一个很好的建议,但我们如何知道一个类是否或如何被设计为可扩展的?

第一个答案是文档(有时以文档字符串甚至代码中的注释的形式)。例如,Python 的 socketserver 包被描述为“网络服务器框架”。顾名思义,它的 BaseServer 类是为子类化而设计的。更重要的是,类的源代码中的文档和文档字符串明确指出其哪些方法可以被子类覆盖。

在 Python ≥ 3.8 中,PEP 591—Adding a final qualifier to typing 提供了一种明确设计约束的新方法。PEP 引入了一个 @final 装饰器,可以应用于类或单个方法,以便 IDE 或类型检查器可以报告对这些类进行子类化或覆盖这些方法的错误尝试。

7. 避免继承多个具体类

子类化具体类比子类化 ABC 和 mixin 更危险,因为具体类的实例通常具有内部状态,当您覆盖依赖于该状态的方法时,这些状态很容易被破坏。即使您的方法通过调用 super() 进行协作,并且使用 __x 语法将内部状态保存在私有属性中,方法覆盖仍然有无数种方式会引入错误。

在“Waterfowl and ABCs”中,Alex Martelli 引用了 Scott Meyer 的 More Effective C++,其中说:“所有非叶节点类都应该是抽象的”。换句话说,Meyer 建议只对抽象类进行子类化。

如果您必须使用继承进行代码重用,那么用于重用的代码应该在 ABC 的 mixin 方法中或在显式命名的 mixin 类中。

我们现在将从这些建议的角度分析 Tkinter。

Tkinter:好的、不好的和令人厌恶的方面

Note:

请记住,自 1994 年 Python 1.1 发布以来,Tkinter 一直是标准库的一部分。Tkinter 的底层是 Tcl 语言的优秀 Tk GUI 工具包。Tcl/Tk 组合最初不是面向对象的,因此 Tk API 基本上是一个庞大的函数目录。但是,如果不是在其原始实现使用了 Tcl ,该工具包的设计理念中是面向对象的。

Tkinter 没有遵循上一节中的大多数建议,除了“5. Provide Aggregate Classes to Users”。即便如此,这也不是一个很好的例子,因为组合可能会更好地将几何管理器集成到 Widget 中,如“1. Favor Object Composition Over Class Inheritance”。

tkinter.Widget 的文档字符串以“内部类”字样开头。这表明 Widget 应该是一个 ABC。它传达的信息是:“除了所有三个几何管理器的全部方法之外,您还可以依靠每个 Tkinter Widget提供基本的小组件方法(__init__、destroy 和数十个 Tk API 函数)。”我们可以同意这不是一个很好的接口定义(它太宽泛了),但它是一个接口,而 Widget 将它“定义”为它的超类接口的集合。

封装了 GUI 应用程序逻辑的 Tk 类继承自 Wm 和 Misc,这两个基类既不是抽象的也不是 mixin(Wm 不是一个合适的 mixin,因为 TopLevel 的超类只有Wm)。Misc 类的名称本身就是一种非常强烈的代码异味。Misc 有 100 多个方法,所有小组件都继承自它。为什么每个小组件都需要有用于剪贴板处理、文本选择、计时器管理等的方法?您无法按钮中进行粘贴或从滚动条中选择文本。 Misc 应该被分成几个专门的 mixin 类,并不是所有的小组件都应该继承自所有的 mixin 。

公平地说,作为 Tkinter 用户,您根本不需要知道或使用多重继承。它是隐藏在小组件类后面的实现细节,您只需要自己的代码中实例化或子类化小组件。但是当您键入 dir(tkinter.Button) 并尝试在列出的 214 个属性中找到您需要的方法时,您将承受过度使用多重继承的后果。如果您决定实现一个新的 Tk 小组件,您将需要面对Widget的复杂性。

TIP:

尽管存在一些问题,但如果您使用 tkinter.ttk 包及其主题小组件时,Tkinter 是稳定、灵活的,并提供现代外观和感觉。此外,一些原始小部件,如 Canvas 和 Text,非常强大。您可以在几个小时内将 Canvas 对象打造成一个简单的拖放绘图应用程序。如果您对 GUI 编程感兴趣,Tkinter 和 Tcl/Tk 绝对值得一看。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. echarts 实现背景 音量 柱状图

    带有背景高度变化的柱状图&#xff0c;有跳动的效果。 option {tooltip: {trigger: axis},legend: {},grid: {left: 3%,right: 4%,bottom: 3%,containLabel: true},xAxis: [{type: category,data: [Z1, Z2, Z3]}],yAxis: [{type: value}],series: [{name: WATER,type: bar,co…...

    2024/5/5 22:56:22
  2. N3-PEG4-C2-NHS ester,944251-24-5和不同的英文别名汇编

    英文名称&#xff1a;Azido-PEG4-NHS Ester N3-PEG4-NHS 英文别名&#xff1a;N-Succinimidyl 15-Azido-4,7,10,13-tetraoxapentadecanoate NHS-PEG4-Azide N3-PEG4-C2-NHS ester N3-DPEG(4)-NHS MFCD13184948 中文名称&#xff1a;叠氮四聚乙二醇琥珀酰亚胺&#xff1b…...

    2024/5/5 21:26:59
  3. DG逻辑备库无法同步日志报错ORA-01658处理

    作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;目前从事DBA及程序编程 &#xff08;Web\java\Python&#xff09;工作&#xff0c;主要服务于生产制造 现拥有 Oracle 11g OCP/OCM、 Mysql、Oceanbase&#xff08;OBCA&#xff09;认证 分布式TBase\TDSQL数据库、国…...

    2024/5/5 19:07:06
  4. 网络文件系统和网络存储

    网络文件系统可以实现存储分离 1、windows文件共享 windows文件共享不做演示 2、如何在linux里访问windows文件共享&#xff1f; [rootlocalhost ~]# dnf install samba-client -y 安装客户端 [rootlocalhost ~]# smbclient -L //172.25.254.94 -U administrator window…...

    2024/4/19 4:23:25
  5. 【docker】 docker import导入docker export导出的容器

    【docker】 docker import导入docker export导出的容器 1、背景2、导入容器的tar文件3、示例1、背景 docker export导出的容器tar文件,再次导入到docker软件中需要使用docker import 命令。 docker import 将container容器tar文件导入后,恢复成一个image镜像。 相比于docke…...

    2024/5/5 7:09:44
  6. smplify-x 复现记录

    其实已经配过一遍了&#xff0c;但是ubuntu,cuda,pytorch,没按readmel来弄&#xff0c;用的ubuntu20.0.4cuda11.0pytorch1.7配了环境&#xff0c;网络可以跑&#xff0c;但是文章里面用到的自渗透检测的方法要求的pytorch版本很低,要求pytorch1.0,高了编译不了,会报一个Compile…...

    2024/5/5 17:44:48
  7. C:大数加法

    问题描述&#xff1a;两个很大很大的数相加&#xff0c;例如&#xff1a; 111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111110 思路&#xff1…...

    2024/4/16 3:49:00
  8. 水洼数目(dfs)

    水洼数目有一个大小为N * M的院子&#xff0c;雨后积起了水&#xff0c; 八连通的积水被认为是连在一起的&#xff0c;请求出园子里面总共有多少水洼&#xff08;八连通指的是下图中相对w大的*部分&#xff09; *** *w* *** 限制条件 N, M <100 样例&#xff1a; 输入&#…...

    2024/5/6 0:11:03
  9. RL2021有关机器学习和强化学习的相关内容总结(草稿版一稿)

    机器学习强化学习总结 一、机器学习模型框架 模型学习&#xff1a; 数据&#xff1a;测试集、训练集 训练集-模型训练-模型评估 模型使用&#xff1a;输入数据-通过训练好的模型-输出 模型训练过程&#xff1a;1.模型选择&#xff08;通过人类经验选择选择更合适的模型&…...

    2024/4/19 19:28:58
  10. ROS系列——如何把ROS和STM32之间联系起来

    ROS系列——如何把ROS和STM32之间联系起来 本节内容包括如何实现ros主控和stm32之间的通信&#xff0c;以及ros主控对stm32发送的数据做了哪些处理 一. 两种控制器的功能 1.1 ROS主控实现的功能 ①雷达信息采集 ②摄像头信息采集 ③路径规划1.2 STM32控制器实现的功能 ①里…...

    2024/4/14 20:25:42
  11. Java入门

    Java入门 java 特性&#xff1a; 简单性&#xff0c;java是c语法的纯净版&#xff0c;没有头文件&#xff0c;指针运算&#xff0c;而且也不需要分配内存。语法基于C。面向对象&#xff0c;是程序设计技术。将重点放在接口和接口的对象之上。模拟的是人的思维去写程序。万物皆…...

    2024/4/14 20:26:12
  12. Python爬取B站历史观看记录并用Bokeh做数据可视化

    待爬取的数据 爬虫代码 import os import time import requests import pandas as pd# cookie 用浏览器登录B站&#xff0c;按F12打开开发人员工具&#xff0c;找到自己的cookie替换 cookies_dict {_uuid: "1C7F0395-1CDC-5BBF-E859-528F14EA305F09211infoc",bili_…...

    2024/4/17 22:25:57
  13. ROS系列——ROS话题

    ROS系列——ROS话题 话题通信是ROS最常用以及最基础的通信方法 1. Ros话题通信机制&#xff1a; 话题通信是一种支持一对多的异步通信机制&#xff0c;一般来说话题通信有一个发布者和一个订阅者&#xff0c;发布者将信息发布到话题上&#xff0c;订阅者从话题订阅信息&#…...

    2024/4/17 0:38:24
  14. SDRAM

    简介、优缺点、历史 1、译为“同步动态随机存取内存”&#xff0c;区别于异步DRAM。 2、同步(Synchronous)&#xff1a;与通常的异步 DRAM 不同&#xff0c; SDRAM 存在一个同步接口&#xff0c;其工作时钟的时钟频率与对应控制器(CPU/FPGA)的时钟频率相同&#xff0c;并且 S…...

    2024/4/15 2:54:18
  15. Makefile中执行pwd赋值给变量

    export CUR_DIR$(shell pwd) 参考&#xff1a; Makefile中利用shell的方式来给变量赋值的两种方法_猫瑾的博客-CSDN博客...

    2024/4/14 20:25:47
  16. ROS系列——launch para

    ROS系列——launch para launch文件&#xff1a;可实现多节点启动和参数配置的xml文件 1.launch标签总览 <launch> ... </launch> &#xff1a; 根标签 <node> &#xff1a;启动节点 <include> &#xff1a;嵌套 <remap> &#xff1a;重命名 …...

    2024/4/16 22:12:30
  17. 宜浩服务队隐私政策

    本隐私政策介绍本公司或本人的隐私数据相关政策和惯例&#xff0c;这将涵盖我们如何收集、使用、处理、存储和/或披露那些通过本公司的移动App收集的关于您的个人信息。请你仔细阅读我们的隐私政策。 一、如何收集您的个人信息 个人信息是可用于唯一地识别或联系某人的数据。…...

    2024/4/14 20:26:17
  18. 第十七届全国大学生智能车竞赛第一次组委会扩大会议在线上召开

    在今天&#xff08;2021,11&#xff0c;7&#xff09;上午&#xff0c; 全国大学生智能车竞赛 组委会通过线上举行了第十七届智能车竞赛组委会扩大会议。来自中国自动化学会、竞赛秘书处、各分赛区、省赛区、全国总决赛承办学校以及省赛区挂靠单位负责人参加了此次会议。 会议首…...

    2024/4/14 20:25:52
  19. 基于用户投诉信息的知识图谱构建与实现

    自从google公司推出旗下的产品Knowledge Graph以来&#xff0c;知识图谱这个概念越来越受到学术与工业界的关注。如何以质量参差不齐的网页数据作为原始数据源&#xff0c;构建知识图谱已经成为了一个热门的研究课题。 互联网技术的迅速发展导致了网民数量的快速增长。愈来愈多…...

    2024/4/18 4:47:59
  20. leetcode学习笔记(打家劫舍 II)

    213. 打家劫舍 II 和第192打家劫舍类似&#xff0c;当房间数为1时不用选择&#xff0c;房间数为2时选较大的一家。 在大于2家时由于首尾相连&#xff0c;可以分成两种情况&#xff0c;选中第一家&#xff0c;不选第一家&#xff0c; 选中第一家&#xff1a; 则一定不能选最后一…...

    2024/4/5 5:33:59

最新文章

  1. nginx的前世今生(二)

    书接上回&#xff1a; 上回书说到&#xff0c;nginx的前世今生&#xff0c;这回我们继续说 3.缓冲秘籍&#xff0c;洪流控水 Nginx的缓冲区是其处理数据传输和提高性能的关键设计之一&#xff0c;主要用于暂存和管理进出的数据流&#xff0c;以应对不同组件间速度不匹配的问题…...

    2024/5/6 1:35:52
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. OpenCV单通道图像按像素成倍比例放大(无高斯平滑处理)

    OpenCV中的resize函数可以对图像做任意比例的放大(/缩小)处理&#xff0c;该处理过程会对图像做高斯模糊化以保证图像在进行放大&#xff08;/缩小&#xff09;后尽可能保留源图像所展现的具体内容&#xff08;消除固定频率插值/采样带来的香农采样信息损失&#xff09;&#x…...

    2024/5/5 8:49:58
  4. 流域生态系统水-碳-氮耦合过程模拟

    流域是一个相对独立的自然地理单元&#xff0c;它是以水系为纽带&#xff0c;将系统内各自然地理要素连结成一个不可分割的整体。碳和氮是陆地生态系统中最重要的两种化学元素&#xff0c;而在流域系统内&#xff0c;水-碳-氮是相互联动、不可分割的耦合体。随着流域内人类活动…...

    2024/5/5 8:45:42
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57