python的一些高级用法
生成器和迭代器
在讲生成器之前需要先了解一下一个高级的用法,也是我在面试过程中被问到的一个问题,当时没搞明白面试官要考察我什么,就没答出来,有点尴尬。
问题是:怎么用一行程序生成一个1-10数字的平方的一个列表。
答:用列表生成式,[x * x for x in range(1, 11)]。
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
>>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):File "", line 1, in <module>
StopIteration
当然&#xff0c;上面这种不断调用next(g)实在是太变态了&#xff0c;正确的方法是使用for循环&#xff0c;因为generator也是可迭代对象&#xff1a;
>>> g &#61; (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
迭代器&#xff1a;
凡是可作用于for循环的对象都是Iterable类型&#xff1b;
凡是可作用于next()函数的对象都是Iterator类型&#xff0c;它们表示一个惰性计算的序列&#xff1b;
继承和多态
下面这是父类的定义&#xff1a;
class Animal(object):def run(self):print(&#39;Animal is running...&#39;)
下面是子类继承&#xff1a;
class Dog(Animal):def run(self):print(&#39;Dog is running...&#39;)def eat(self):print(&#39;Eating meat...&#39;)
当子类和父类都存在相同的run()方法时&#xff0c;我们说&#xff0c;子类的run()覆盖了父类的run()&#xff0c;在代码运行的时候&#xff0c;总是会调用子类的run()。这样&#xff0c;我们就获得了继承的另一个好处&#xff1a;多态。
多态也就是子类既有父类的数据类型也有子类的数据类型&#xff0c;但是父类没有子类的数据类型。&#xff08;这里的数据类型就是类名&#xff09;
要理解多态的好处&#xff0c;我们还需要再编写一个函数&#xff0c;这个函数接受一个Animal类型的变量&#xff1a;
def run_twice(animal):animal.run()animal.run()
>>> run_twice(Dog())
Dog is running...
Dog is running...
>>> run_twice(Animal())
Animal is running...
Animal is running...
你会发现&#xff0c;新增一个Animal的子类&#xff0c;不必对run_twice()做任何修改&#xff0c;实际上&#xff0c;任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行&#xff0c;原因就在于多态。
多态的好处就是&#xff0c;当我们需要传入Dog、Cat、Tortoise……时&#xff0c;我们只需要接收Animal类型就可以了&#xff0c;因为Dog、Cat、Tortoise……都是Animal类型&#xff0c;然后&#xff0c;按照Animal类型进行操作即可。由于Animal类型有run()方法&#xff0c;因此&#xff0c;传入的任意类型&#xff0c;只要是Animal类或者子类&#xff0c;就会自动调用实际类型的run()方法&#xff0c;这就是多态的意思&#xff1a;
对于一个变量&#xff0c;我们只需要知道它是Animal类型&#xff0c;无需确切地知道它的子类型&#xff0c;就可以放心地调用run()方法&#xff0c;而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上&#xff0c;由运行时该对象的确切类型决定&#xff0c;这就是多态真正的威力&#xff1a;调用方只管调用&#xff0c;不管细节&#xff0c;而当我们新增一种Animal的子类时&#xff0c;只要确保run()方法编写正确&#xff0c;不用管原来的代码是如何调用的。这就是著名的“开闭”原则&#xff1a;
对扩展开放&#xff1a;允许新增Animal子类&#xff1b;
对修改封闭&#xff1a;不需要修改依赖Animal类型的run_twice()等函数。
装饰器
这种在代码运行期间动态增加功能的方式&#xff0c;称之为“装饰器”&#xff08;Decorator&#xff09;。
装饰器的作用就是为已经存在的对象添加额外的功能。
具体用到的时候我有了更深的理解再补充
注册器
这个一般出现在比较大型的项目中。
这个注册器不是用户账号注册的模块&#xff0c;而是项目中注册模块的一个模块。举个例子&#xff0c;一个深度学习项目可能支持多种模型&#xff1b;具体使用哪种模型可能是用户在配置文件中指定的。最简单的实现方式&#xff0c;就是维护一个模型名称->模型类的字典。但每当你增加一个模型时&#xff0c;这个字典就需要手动维护&#xff0c;比较繁琐。本文介绍一种注册器的模块&#xff0c;你需要维护的是需要注册的模块的代码路径&#xff08;相对简介些&#xff09;。
其实说的简单点就是注册器就是一个自动维护的字典&#xff0c;将你定义的模块添加到项目中。
class Register:def __init__(self, registry_name):self._dict &#61; {}self._name &#61; registry_namedef __setitem__(self, key, value):if not callable(value):raise Exception(f"Value of a Registry must be a callable!\nValue: {value}")if key is None:key &#61; value.__name__if key in self._dict:logging.warning("Key %s already in registry %s." % (key, self._name))self._dict[key] &#61; valuedef register(self, target):"""Decorator to register a function or class."""def add(key, value):self[key] &#61; valuereturn valueif callable(target):return add(None, target)return lambda x: add(target, x)def __getitem__(self, key):return self._dict[key]def __contains__(self, key):return key in self._dictdef keys(self):"""key"""return self._dict.keys()
主要看一下register函数&#xff0c;其实就是一个add模块。