mixin

介绍

Mixin 是一种软件开发中的设计模式,它通过将可重用的功能以独立的模块形式提供,并通过混合(或混入)的方式将这些功能添加到其他类中,以实现代码的复用和组合。

Mixin 提供了一种在不继承的情况下将功能添加到类中的方法,这样可以避免单继承带来的局限性。Mixin 类通常只包含一个或多个相关的方法,这些方法可以在其他类中重复使用。

使用 Mixin 可以在类之间共享和复用代码,同时保持类之间的独立性和灵活性。它可以用于添加各种功能,如日志记录、缓存、权限验证、序列化等,以及其他通用的横切关注点。

在 Python 中,Mixin 通常是一个普通的类,它定义了一组方法或属性。为了将 Mixin 的功能添加到其他类中,可以使用多重继承,并将 Mixin 类放在继承列表的前面。这样,在方法查找时,Python 解释器会首先在 Mixin 类中查找方法,然后才会在后续的父类中查找。

Mixin 的优点包括:

  • 代码复用:Mixin 提供了一种将功能模块化并在多个类中共享和复用的方法。
  • 灵活性:通过混入不同的 Mixin,可以根据需要组合功能,使类具有灵活的行为。
  • 避免继承的限制:Mixin 可以在不继承的情况下将功能添加到类中,避免了单继承带来的局限性。

然而,使用 Mixin 时也需要注意一些问题,例如命名冲突、方法调用顺序、多重继承带来的复杂性等。因此,在使用 Mixin 时需要谨慎设计和组织代码,以确保正确性和可维护性。

应用上下文

Application Context优势:

  • 不修改源码前提下, 对已有类进行扩展;
  • 确保各个组件的清晰划分;
  • 使用已有的功能来组合, 实现”新类”;

使用场景:

  • You want to provide a lot of optional features for a class.
  • You want to use one particular feature in a lot of defferent class.

示例

Werkzeug包里面的使用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from werkzeug import BaseRequest

class Request(BaseRequest):
pass

# add accept header support
from werkzeug import AcceptMixin
class Request(BaseRequest, AcceptMixin):
pass

# add others
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin):
pass

Request包含很多mixin基类, 导致整个Request本地无需实现太多逻辑. mixin的目的:

  • 不是为了实例化而创建,不是自包含自运行的类, 里面有些方法甚至都是未定义就先使用了.
  • 职责非常单一
  • 必须结合所需映射功能的类才能”自运行”

Lazy

惰性取值

在编程中,”lazy”(懒加载)是一种策略,用于推迟计算或加载操作的执行,直到需要的时候才进行。这种延迟执行的机制可以提高性能和资源利用率,特别是在处理大量数据或耗时操作时。

与懒加载相关的概念是”惰性求值”,它是指只有在需要的时候才会进行计算或操作,而不是立即执行。这种延迟计算的特性可以节省时间和资源,并在需要时提供所需的数据或结果。

与 mixin 相关联的地方在于,Mixin 类可以使用懒加载的方式提供额外的功能。Mixin 可以包含惰性计算的属性或方法,只有在需要的时候才会计算或执行相应的操作。这样可以延迟计算的开销,并根据需要动态添加功能。

举个例子,假设有一个 Mixin 类 LazyInitializationMixin,它包含一个惰性计算的属性 data。该属性的值在第一次访问时才会计算,然后缓存起来以供后续使用。这种惰性加载的方式可以避免在创建实例时立即执行复杂的计算或获取大量数据,而是在实际需要时才进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class LazyInitializationMixin:
@property
def data(self):
if not hasattr(self, '_data'):
# 执行复杂的计算或获取大量数据
self._data = self._calculate_data()
return self._data

def _calculate_data(self):
# 计算或获取数据的逻辑
pass

class MyClass(LazyInitializationMixin):
pass

# 使用 MyClass
obj = MyClass()
# 在此处第一次访问 data 属性,会触发计算或获取数据的操作
print(obj.data)

在上面的示例中,LazyInitializationMixin 提供了一个懒加载的属性 data。当创建 MyClass 的实例并访问 data 属性时,会执行 calculate_data() 方法来计算或获取数据,并将结果缓存起来。这样,只有在需要使用 data 数据时才会进行计算,避免了不必要的开销。

因此,虽然 mixin 和 lazy 是不同的概念,但在一些情况下可以结合使用 mixin 来提供懒加载的功能,从而实现代码的复用和延迟计算的优势。

短路求值

短路求值是一种逻辑运算的特性,在执行逻辑运算时,如果根据已经计算的部分结果就能确定整个表达式的结果,那么就不再计算剩余的部分。这种机制可以提高程序的效率,避免不必要的计算。

短路求值与惰性取值有一定的关联,因为它们都涉及到在需要时进行计算的策略。

具体来说,与惰性取值相关的是表达式求值中的短路求值。短路求值的特性可用于逻辑运算符(如逻辑与 and 和逻辑或 or)的计算中。在逻辑与运算中,如果第一个操作数为假(False),则整个表达式的结果必定为假,因此不再计算第二个操作数。类似地,在逻辑或运算中,如果第一个操作数为真(True),则整个表达式的结果必定为真,不再计算第二个操作数。这种延迟计算的策略与惰性取值的概念相似,即只有在需要时才进行计算。

下面是一些示例来说明短路求值的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 短路求值示例
x = 5
y = 0

# 逻辑与运算
result = x > 0 and y > 0
# 因为 x > 0 为真,但 y > 0 为假,所以不再计算 y > 0,结果为假
print(result) # 输出: False

# 逻辑或运算
result = x > 0 or y > 0
# 因为 x > 0 为真,不再计算 y > 0,结果为真
print(result) # 输出: True

在上述示例中,逻辑与运算中的第一个操作数 x > 0 为真,但是第二个操作数 y > 0 为假,因此整个表达式的结果为假,不再计算第二个操作数的值。类似地,逻辑或运算中的第一个操作数 x > 0 为真,因此整个表达式的结果为真,不再计算第二个操作数的值。

这种短路求值的特性在编程中常常被用于条件判断和控制流程,可以避免不必要的计算和函数调用,提高代码的效率和性能。

重定义

在编程中,”Redefined”(重新定义)指的是对已有的变量、函数、类或其他实体进行了新的定义或赋值。当一个实体被重新定义时,原有的定义将被覆盖或替代,新的定义将会生效。

  1. 变量的重新定义:当一个变量被重新赋值时,它的原有值将被新的值所替代。这样,变量的值会随着重新赋值的操作而改变。例如:

    1
    2
    x = 10
    x = 20 # x 被重新定义为 20
  2. 函数的重新定义:当一个函数被重新定义时,新的函数定义将会取代原有的函数定义。这样,在调用函数时会执行新的函数体内的代码逻辑。例如:

    1
    2
    3
    4
    5
    def greet():
    print("Hello, world!")

    def greet():
    print("Bonjour, le monde!") # greet 函数被重新定义为使用法语打招呼
  3. 类的重新定义:当一个类被重新定义时,新的类定义将会覆盖原有的类定义。这样,在创建类的实例时会使用新的类定义来构建对象。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Shape:
    def area(self):
    return 0

    class Shape:
    def area(self):
    return 10 # Shape 类被重新定义,area 方法返回固定值 10

    # 利用type创建新类就不是重定义
    class A(object):
    pass
    # 利用type来创建新的类
    NEWA = type('A', (object,), dict())

通过重新定义变量、函数或类,开发者可以修改它们的行为、属性或方法,从而达到定制、扩展或修改功能的目的。然而,需要注意在重新定义时避免引发命名冲突或不必要的混淆。

元类

隐藏逻辑

对于如下代码

1
2
class Foo(bar):
pass

解释器内部通过元类来执行以下步骤:

  • 解析基类
  • 创建命名空间
  • 执行类定义
  • 创建类对象
  • 初始化类对象以及类属性的绑定

其中类对象的创建就是使用type元类来完成的, 那么什么是元类呢? 元类一般有什么用途呢? 元类的使用场景都有哪些?

分类

在Python中,有两种常见的方式用于创建元类:使用内置的type函数和定义自定义的metaclass类。

  1. 使用type函数创建元类:

    • type函数是Python的内置函数,它可以用于动态地创建类和对象。通过传递三个参数给type函数:类名、基类元组和属性字典,可以创建一个新的类。
    • 在创建元类时,我们直接调用type函数并将类名、基类元组和属性字典作为参数传递给它。type函数会返回一个新的类对象,该类对象是元类的实例。
    • 这种方式简单、直接,并且适用于大多数情况。
    1
    2
    3
    4
    5
    # 类名-MyMetaClass, 基类元组: (type,), 属性字典: {}
    MyMetaClass = type("MyMetaClass", (type,), {})

    class MyClass(metaclass=MyMetaClass):
    pass
  2. 定义自定义的metaclass类:

    • 自定义的metaclass类是一个普通的Python类,它继承自type类或其子类,并重写其中的特殊方法,用于定制类的创建过程和行为。
    • 在创建元类时,我们定义一个自定义的metaclass类,重写其中的特殊方法,例如__new____init____call__等。这些方法会在类的创建过程中被调用,允许我们对类进行定制。
    • 这种方式更加灵活和强大,可以实现更复杂的元编程需求。
    1
    2
    3
    4
    5
    6
    7
    8
    class MyMetaClass(type):
    def __new__(cls, name, bases, attrs):
    # 在创建类时进行一些操作
    # ...
    return super().__new__(cls, name, bases, attrs)

    class MyClass(metaclass=MyMetaClass):
    pass

总结比较:

  • 使用type函数创建元类简单直接,适用于大多数场景。
  • 定义自定义的metaclass类更加灵活和强大,允许在类的创建过程中定制更复杂的行为。
  • 使用type函数创建元类时,无需定义新的类,可以直接使用type函数返回的类对象。
  • 定义自定义的metaclass类时,需要创建一个新的类,并继承自type类或其子类。
  • 使用type函数创建元类时,属性字典可以包含类的方法、属性和其他内容。
  • 定义自定义的metaclass类时,可以重写特殊方法来定制类的创建过程,并可以在其中进行更复杂的操作。

根据具体的需求和场景,选择使用type函数创建元类还是定义自定义的metaclass类。大多数情况下,使用type函数已经足够满

足元编程的需求。

使用场景

元类的使用场景是在需要对类进行高度定制和控制的情况下。它提供了一种元编程的能力,允许在类的创建过程中动态地修改、定制和操作类的行为、属性和方法。以下是一些常见的使用元类的场景:

  1. 框架和库的开发:在框架和库的设计中,元类常用于创建特定的基类或接口,以及在类定义阶段对用户定义的类进行验证和装饰。它可以用于实现框架的特定功能、约束和规范。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class PluginMeta(type):
    def __init__(cls, name, bases, attrs):
    if not attrs.get('plugin_name'):
    raise ValueError("插件必须有名称, 否则无法创建")
    super().__init__(name, bases, attrs)

    class Plugin(metaclass=PluginMeta):
    plugin_name = None

    class MyPlugin(Plugin):
    plugin_name = "MyPlugin" # 正确的定义

    class InvalidPlugin(Plugin):
    pass # 抛出异常,缺少 plugin_name 属性
  2. 序列化和反序列化:元类可以用于自定义对象的序列化和反序列化过程。通过在元类中定义特殊的方法,可以控制对象的序列化方式、字段的映射规则等。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class SerializableMeta(type):
    def __new__(cls, name, bases, attrs):
    attrs['_fields'] = [name for name, value in attrs.items() if isinstance(value, Field)]
    return super().__new__(cls, name, bases, attrs)

    class Field:
    def __init__(self, name):
    self.name = name

    class Serializable(metaclass=SerializableMeta):
    def serialize(self):
    data = {}
    for field in self._fields:
    data[field] = getattr(self, field)
    return data

    class Person(Serializable):
    name = Field('name')
    age = Field('age')

    def __init__(self, name, age):
    self.name = name
    self.age = age

    person = Person("John Doe", 25)
    serialized_data = person.serialize()
  3. API接口的自动化生成:元类可以用于自动生成API接口,根据类的定义自动创建路由、验证规则等。它可以简化接口开发过程,并提高代码的复用性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class APIMeta(type):
    def __new__(cls, name, bases, attrs):
    api_routes = []
    for attr_name, attr_value in attrs.items():
    if isinstance(attr_value, APIRoute):
    api_routes.append(attr_value)
    attrs['_api_routes'] = api_routes
    return super().__new__(cls, name, bases, attrs)

    class APIRoute:
    def __init__(self, path, method):
    self.path = path
    self.method = method

    class APIController(metaclass=APIMeta):
    pass

    class UserController(APIController):
    @APIRoute('/users', 'GET')
    def list_users(self):
    pass

    @APIRoute('/users', 'POST')
    def create_user(self):
    pass

    controller = UserController()
    api_routes = controller._api_routes

这些示例只是一小部分展示了元类的使用场景。实际上,元类的应用非常灵活,可以根据具体需求进行定制和扩展。使用元类可以实现高度定制的类创建过程,并在开发中提供更强大的元编程能力。

元数据

介绍

元数据(Metadata)是关于数据的描述信息。它提供了关于数据的属性、特征、含义或其他与数据相关的信息。元数据可以帮助我们理解和解释数据,提供上下文信息,以及支持数据的处理和管理。

在计算机科学中,元数据可以应用于各种领域和数据类型,包括文件、数据库、网络资源、代码等。它可以描述数据的结构、格式、来源、用途、权限、有效期等方面的信息。

以下是一些示例,展示了元数据的不同应用场景和形式:

  1. 文件元数据:

    • 文件名、路径和大小
    • 创建日期和修改日期
    • 文件类型、格式和扩展名
    • 文件所有者和权限
    • 文件的摘要、标签或关键字
  2. 数据库元数据:

    • 表名、列名和索引
    • 列的数据类型、长度和约束
    • 表之间的关系和连接
    • 数据库的版本和备份信息
    • 数据的来源和质量指标
  3. 网络资源元数据:

    • URL、URI和资源路径
    • MIME类型和媒体格式
    • 协议和加密方式
    • 访问控制和权限设置
    • 作者、版权和许可证信息
  4. 代码元数据:

    • 类和函数的文档字符串(docstring)
    • 注释和标签
    • 类和函数的参数和返回值描述
    • 版本号和更新日志
    • 依赖关系和导入模块的信息

元数据可以以各种形式存在,如文本、标记语言、注释、属性等。它可以直接嵌入在数据本身中,存储在独立的文件或数据库中,或者作为附加信息与数据关联。

以下是一个示例,展示了一个图像文件的元数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
image_metadata = {
'filename': 'image.jpg',
'size': (1024, 768),
'format': 'JPEG',
'resolution': '300 dpi',
'author': 'John Doe',
'keywords': ['nature', 'landscape', 'mountain']
}

# 访问元数据
print(image_metadata['filename'])
print(image_metadata['author'])
print(image_metadata['keywords'])

在上面的示例中,我们使用字典表示图像文件的元数据。元数据包括文件名、大小、格式、分辨率、作者和关键字等信息。我们可以通过键来访问和提取特定的元数据,以了解有关图像文件的更多信息。

总而言之,元数据是描述数据的数据,提供关于数据的属性、特征、含义或其他相关信息。它在各个领域中起着重要作用,帮助我们理解和管理数据。

数据和元类

元数据(Metadata)是描述数据的数据。它提供关于数据的信息,如数据的类型、结构、属性、约束等。在编程中,元数据可以用于描述类、函数、变量等程序元素的信息。

元类(Metaclass)与元数据有密切关联。元类是用于创建类对象的类,它可以在类定义过程中拦截和操作类的创建过程。通过元类,我们可以控制类对象的行为,并向类对象添加元数据。元类可以使用元数据来描述类对象的属性、方法、注释等信息。以下是一个示例,演示了元类与元数据的关联:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Meta(type):
def __new__(cls, name, bases, attrs):
# 在类对象创建之前,可以对元数据进行操作
attrs['meta_data'] = {
'author': 'John',
'version': 1.0,
'description': 'This is a sample class'
}
return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=Meta):
pass

obj = MyClass()
print(obj.meta_data)

在上面的示例中,我们定义了一个元类Meta,它在创建类对象时会添加额外的元数据到类的属性中。在Meta__new__方法中,我们可以通过attrs参数访问到类定义过程中的属性字典,并在其中添加元数据。在MyClass定义时,我们指定了Meta作为元类,因此它会被用于创建MyClass类对象。

当我们实例化MyClass并访问其meta_data属性时,我们可以获取到添加的元数据信息。这个元数据可以包含有关类对象的任意信息,如作者、版本、描述等。通过元类和元数据的结合使用,我们可以在类定义过程中添加额外的信息,以及在运行时对类对象进行更高级的操作和扩展。

web框架

没有最好的框架, 只有适合的框架. 在项目不同的阶段, 随着需求, 请求, 框架可能都发生变动, 技术永远都是始于实践, 臣服于实践, 忠于实践.

  1. django
  • 高级的python web框架, 提供从模块引擎到ORM 所需的一切,包含ORM、强大的后台管理界面、全面的文档和大型社区支持,包含很多内置功能,如用户认证、内容管理、RSS、站点地图等

  • Advantage: 高度集成, 快速开发, 简洁, 实用.

  • Application: 电子商务

  • Rule: 约定优于机制

  • Relation Frame: Ruby on Rails

  1. flask
  • 基于 Werkzeug, Jinja 2 的 Python 轻量级框架(microframework), 轻量且成熟,用于小型项目和微服务,使用 WSGI 工具包和 Jinja2 模板引擎进行简单和快速开发

  • Advantage: 轻量, 自由搭配

  • Application: Restful services.

  1. tornado
  • Python web框架和异步网络库, 分四个部分: web框架, HTTP server/client实现, 异步网络库, 协程库

  • Advantage: 非阻塞网络 I / O 模型, 可以处理数以千计的网络连接. 处理C10K(服务器同时支持并发10K 量级的连接)连接

  • Application: 对于 long polling 、WebSockets 和其他需要长时间实时连接的 apps

  1. pyramid
  • Pylons 项目的一部分,其非常灵活,适用于各种规模的应用程序,适合中大型应用程序,强调可扩展性,比如用于简单的应用并随项目成长,其包含强大的配置系统,
  • Advantage: 高度灵活
  • Application: 复杂应用程序和CMS系统、高度定制化的项目

参考