Blog

利用Python生成器解决判断子序列问题

Question #

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,“ace"是"abcde"的一个子序列,而 “aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

示例 1:

输入:s = “abc”, t = “ahbgdc” 输出:true 示例 2:

输入:s = “axc”, t = “ahbgdc” 输出:false

Answer #

def isSubsequence(self, s, t):
  t = iter(t)
  return all(i in t for i in s)

Viewpoint #

生成器的遍历只能往前,本题的解法充分利用了这一点。其中"i in t"的效果与下面的代码功能相似:

while True:
    val = next(t)
    if val == i:
        yield True

在t中找到了val后,生成器会记信这个位置,下次再执行"i in t"的判断时,会继续往下找。生成器相当于提供了一个免费的带记忆的指针。

...

metaclass是type的子类

Question #

Python类型模型的三条原则之三: metaclass 是 type 的子类,通过替换 type 的__call__运算符重载机制,“超越变形”正常的类。如何理解原则三?

Answer #

正是 Python 的类创建机制,给了 metaclass 大展身手的机会。一旦你把一个类型 MyClass 的 metaclass 设置成 MyMeta,MyClass 就不再由原生的 type 创建,而是会调用 MyMeta 的__call__运算符重载。

class = type(classname, superclasses, attributedict)
# 变为了
class = MyMeta(classname, superclasses, attributedict)

所以,我们才能在上面 YAML 的例子中,利用 YAMLObjectMetaclass 的__init__方法,为所有 YAMLObject 子类偷偷执行add_constructor()。

Viewpoint #

JavaScript中通过修改prototype继承链条,也能实现对基础类的改造?(TODO:找资料证实,添加链接)

From #

18 | metaclass,是潘多拉魔盒还是阿拉丁神灯?

Python Data Model

用户自定义类是type类的__call__运算符重载

Question #

Python类型模型的三条原则之二: 用户自定义类,只不过是 type 类的__call__运算符重载。如何理解这条规则?

Answer #

当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type 的__call__运算符。简单来说,当你定义一个类时,写成下面这样时:

class MyClass:
  data = 1

Python 真正执行的是下面这段代码:

class = type(classname, superclasses, attributedict)

这里等号右边的type(classname, superclasses, attributedict),就是 type 的__call__运算符重载,它会进一步调用:

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

当然,这一切都可以通过代码验证,比如下面这段代码示例:

class MyClass:
  data = 1

instance = MyClass()
MyClass, instance
# 输出
(__main__.MyClass, <__main__.MyClass instance at 0x7fe4f0b00ab8>)
instance.data
# 输出
1
MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
MyClass, instance
# 输出
(__main__.MyClass, <__main__.MyClass at 0x7fe4f0aea5d0>)
instance.data
# 输出
1

由此可见,正常的 MyClass 定义,和你手工去调用 type 运算符的结果是完全一样的。

...

Python自定义类是type类的实例

Question #

Python类型模型的三条原则之一:所有的 Python 的用户定义类,都是 type 这个类的实例。如何理解这句话?

Answer #

事实上,类本身不过是一个名为 type 类的实例。在 Python 的类型世界里, type 这个类就是造物的上帝。这可以在代码中验证:

# Python 3和Python 2类似
class MyClass:
  pass
instance = MyClass()
type(instance)
# 输出
<class '__main__.C'>
type(MyClass)
# 输出
<class 'type'>

你可以看到,instance 是 MyClass 的实例,而 MyClass 不过是“上帝”type 的实例。

From #

18 | metaclass,是潘多拉魔盒还是阿拉丁神灯?

Python Data Model

Python装饰器保持函数元数据

Question #

Python中装饰后的函数的元数据与原函数没有关联,如果要让装饰后的函数与原函数有着一致的元数据,应如何操作?

Answer #

内置的装饰器@functools.wrap,它会帮助保留原函数的元信息(也就是将原函数的元信息,拷贝到对应的装饰器函数里)。

import functools
def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(message):
    print(message)
greet.__name__
# 输出
'greet'

类装饰器 #

From #

17 | 强大的装饰器

Python类装饰器

Question #

Python类装饰器的用法。举例说明。

Answer #

类装饰器主要依赖于函数_call_(),每当你调用一个类的示例时,函数_call_()就会被执行一次。我们来看下面这段代码:

class Count:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0
    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print('num of calls is: {}'.format(self.num_calls))
        return self.func(*args, **kwargs)
@Count
def example():
    print("hello world")
example()
# 输出
num of calls is: 1
hello world
example()
# 输出
num of calls is: 2
hello world
...

这里,我们定义了类 Count,初始化时传入原函数 func(),而_call_()函数表示让变量 num_calls 自增 1,然后打印,并且调用原函数。因此,在我们第一次调用函数 example() 时,num_calls 的值是 1,而在第二次调用时,它的值变成了 2。

From #

17 | 强大的装饰器