2017年末总结

2017 年末总结 :amazed:

author: huchi

文体: 呼式体

让我离20岁更近一步的一年过去了,这一年我学到了很多东西,在点外卖方面有了很多不错的经验以及一些special的视角和实战经验。本来这一年也应该是平平淡淡过去的,写这个总结的原因是因为之前刚刚点了一旦外卖,有感而发。

一来谈谈自己一年所学的成果,二来谈谈自己的刚刚点的外卖。

python设计模式(四)

MVC 模式

模式不是独立工作的,模式通常需要同时使用并加以组合,以实现特定的设计解决方法。

MVC不仅是一种实现用户界面的软件模式,也是一种易于修改维护的架构。通常分为3个基本部分:Model,View,Control。

工作机制:模型提供数据和业务逻辑(如存储查询信息),视图负责数据的展示,控制器是两者的粘合剂,根据用户需求来协调模型和视图。模型可以独立工作,视图和控制器依赖于模型。

MVC涉及的主要类:

  • 模型类定义针对数据的所有操作,并提供与数据使用方法有关的方法
  • 视图类代表用户界面。他提供相应的方法,帮助我们根据上下文和应用程序的需要来构建Web或GUI界面。不应该包含自己的任何逻辑,只应该显示收到的数据
  • 控制器类从请求接收数据,并将其发送到系统的其他部分,它需要提供用于路由请求的方法。

python设计模式(三)

观察者模式

创建型模式的工作原理是基于对象的创建机制的。由于这些模式隔离了对象的创建细节,使得代码能够与要创建的对象的类型相互独立。

结构型模式用于设计对象和类的结构,从而使他们可以相互协作以获得更大的结构。

行为型模式,主要关注的是对象的责任,用来处理对象之间的交互,以实现更大的功能。

观察者设计模式是最简单的行为型模式之一。

主要目标:

  • 定义了对象之间的一对多的依赖关系,从而使一个对象中的任何改动都将通知给其他依赖对象。
  • 封装主题的核心组件

应用场景

  • 分布式系统中实现事件服务
  • 用作新闻机构框架
  • 股票市场也是观察者模式的一个大型场景

python设计模式(二)

工厂模式:建立创建对象的工厂

“工厂”表示一个负责创建其他类型对象的类。作为一个工厂的类有一个对象以及与它关联的多个方法。客户端使用某些参数调用此方法,之后工厂会据此创建所需类型的对象,然后将他们返回给客户端。

优点:

  • 松耦合性,对象的创建可以独立于类的实现
  • 客户端无需了解创建对象的类,但是照样可以使用它来创建对象,它只需要知道传递的接口、方法和参数,就能够创建所需类型的对象,简化了客户端的实现
  • 可以轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码,最简单的情况下,客户端只需要传递另一个参数就可以了。
  • 工厂还可以重用现有对象。但是如果客户端直接创建对象的话,总是创建一个新的对象。

3种变体:

  • 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑
  • 工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则是交由子类决定的。
  • 抽象工厂模式:能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。

python设计模式(一)

python设计模式

面向对象核心概念

对象

他们表示所开发的应用程序内的实体

实体之间可以通过交互来解决现实中的问题

帮助开发人员表示现实世界中的实体

类可以定义对象的属性和行为,属性是数据成员,行为由成员函数表示。

类包含了构造函数,这些函数的作用是为对象提供初始状态

类就像模版一样,非常易于重复使用。

方法

表示对象的行为。

对属性进行处理,从而实现所需的功能。

fluent python (二)python数据模型

fluent python (二)python数据结构

python的序列类型:

  1. 容器序列:

    list、tuple、collections.deque能存放不同类型的数据

  2. 扁平序列:

    str、bytes、bytearray、memoryview、array.array只能容纳一种类型

容器序列中存放的是他们所包含的任意类型的对象的引用,而扁平序列的存放的仅仅只是值。也就是说前者会对对象本身造成印象,而后者不会。扁平序列是一段连续的内存空间,它里面只能存放如字符、字节和数值这种基础类型。

其中tuple,str,bytes是不可变序列。

列表

列表推导:

fluent python (五)一等函数

fluent python (五)一等函数

“一等对象”的定义:为满足下列条件的程序实体

  1. 在运行时创建
  2. 能赋值给变量或数据结构中的元素
  3. 能作为参数穿给函数
  4. 能作为函数的返回结果

在python中,整数、字符串、字典都是一等对象。人们常把“把函数视为一等对象”称作为“一等函数”。在python中,所有函数都是一等函数。

1
2
3
4
5
6
7
def factorial(n):
"""return n!"""
return 1 if n < 2 else n * factorial(n - 1)

print(factorial(12))
print(factorial.__doc__)
print(type(factorial))

我们利用递归实现了一个math库中的一个函数factorial也就是阶乘函数。__doc__属性是生成对象的帮助文档。

1
2
3
4
5
fact = factorial
print(fact)
print(fact(10))
print(map(fact, range(11)))
print(list(map(fact, range(10))))

我们把函数factorial作为一个变量传给fact,fact具有和factorial一样的功能,我们把这个函数作为参数传给map,并可以得到一个可迭代的对象。

高阶函数

接受函数为参数,或者把函数作为返回结果的函数就是高阶函数,比如map, sorted.在python函数式编程中为人熟知的高阶函数有map,filter,reduce和apply,其中apply已经被淘汰了。如果想使用不定量的参数调用函数,可以直接如fn(*args, **keywords),这样使用

在python3中,map和filter还是内置函数,但是由于引入了列表推导和生成器表达式,他们没这么重要了。

1
2
3
4
5
print(list(map(fact, range(6))))
print([fact(n) for n in range(6)])

print(list(map(fact, filter(lambda n: n % 2, range(6)))))
print([fact(n) for n in range(6) if n % 2 == 1])

可以发现列表推导的反而更容易书写和阅读。

在python3中,map和filter返回生成器(一种迭代器),因此他们直接替代品是生成器表达式。

在python2中,reduce是内置函数,python3中放置到了functools模块了,这个函数最常用于求和。

1
2
3
4
5
6
from functools import reduce
from operator import add
from operator import sub
print(reduce(add, range(100)))
print(reduce(sub, range(100)))
print(sum(range(100)))

如果仅仅只是用于求和的话,我们可以用内置函数sum来实现。

除此之外还有两个常用的归约函数:all(iterable)和any(iterable), 从字面意思上就知道前者需要序列中所有元素都为真才返回真,而后者只需存在。

匿名函数

反转拼写给单词倒序排序。

1
2
words = ['az', 'by', 'cx']
print(sorted(words, key=lambda word: word[::-1], reverse=True))

lambda除了作为参数传给高阶函数一般难以书写或者较难阅读,更多的只是一个语法糖,实质上与def一样,会创建一个函数对象,也是python中几种可调用对象的一种。

可调用对象

除了用户定义的函数,调用预算符( 即() )还可以应用到其他对象上,如果想判断对象能否调用,可以使用callable()函数。

python数据模型文档给出的7种可调用对象:

  1. 用户定义的函数:用def,lambda 表达式创建的
  2. 内置函数: 使用C语言(Cpython)实现的函数,如len,time.strftime;
  3. 内置方法: C语言实现,如dict.get
  4. 方法: 在类的定义体中定义的函数
  5. 类:调用类方法时会创建一个实例,然后运行__init__方法,初始化实例,最后把实例返回给调用方。因为Python没有new运算符,所以调用类相当于调用函数。
  6. 类的实例:如果类定义__call__方法,那么他的实例是可以作为函数调用。
  7. 生成器函数:使用yield关键字的函数或者方法,返回的是生成器对象。

用户定义的可调用类型

任何python对象的都可以表现的像函数,只要实现了__call__实例方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from random import shuffle

class Huchi:
def __init__(self, items):
self._items = list(items)
shuffle(self._items) #打乱

def pick(self):
try:
return self._items.pop()
except IndexError:
print('Huchi is hungry')

def __call__(self):
return self.pick()

if __name__ == "__main__":
littleHuchi = Huchi(range(3))

for i in range(4):
print(littleHuchi())

print(callable(littleHuchi))

函数内省

使用dir可以探知对象所具有的属性。大多数属性是python对象所共有的。

1
2
3
4
class C: pass
obj = C()
def func(): pass
print(sorted(set(dir(func)) - set(dir(obj))))

我们可以看到函数独有而一般类对象所没有的属性。

名称类型说明
__annotations__dict参数和返回值的注解
__call__method-wrapper实现()运算符,即可调用对象协议
__closure__tuple函数闭包,自由变量的绑定(通常是None)
__code__code编译成字节码的函数元数据和函数定义体
__defaults__tuple形式参数的默认值
__get__method-wrapper实现只读描述符协议
__globals__dict函数所在模块中的全局变量
__kwdefaults__dict仅限关键字形式参数的默认值
__name__str函数名称
__qualname__str函数的限定名称

仅限关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def tag(name, *content, cls=None, **attrs):
"""return one or more html tag(s)"""
if cls is not None:
attrs['class'] = cls

if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value)
for attr, value in
sorted(attrs.items()))
else :
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>' % (name, attr_str, c, name) for c in content)
else : return '<%s%s />' % (name, attr_str)

print(tag('br'))
print(tag('p', 'hello', 'huchi', cls='huchi', id=33))

定义函数时若想指定仅限关键字参数,要把这些放在*后

1
2
def f(a, *, b):
return a, b

获取关于参数的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def clip(text, max_len=80):
"""
return the string before max_len or after it
"""
end = None
if len(text) > max_len:
space_before = text.rfind(' ', 0, max_len)
if space_before >= 0:
end = space_before
else:
space_after = text.rfind(' ',max_len)
if space_after >= 0:
end = space_after
if end is None:
end = len(text)
return text[:end].rstrip()

同文件下,我们可以用交互式的解释器来测试一下:

1
2
3
4
5
6
7
8
9
>>> from test import clip
>>> clip.__defaults__
(80,)
>>> clip.__code__
<code object clip at 0x01563020...>
>>> clip.__code__.co_varnames
('text', 'max_len', 'end', 'space_before', 'space_after')
>>> clip.__code__.co_argcount
2

在inspect模块中有更好的方式:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from inspect import signature
>>> sig = signature(clip)
>>> sig
<Signature (text, max_len=80)>
>>> str(sig)
'(text, max_len=80)'
>>> for name, param in sig.parameters.items():
... print(param.kind, ':', name, '=', param.default)
...
...
POSITIONAL_OR_KEYWORD : text = <class 'inspect._empty'>
POSITIONAL_OR_KEYWORD : max_len = 80

显然inspect._empty表示没有默认值。kind属性的值是_ParameterKind类中的5个值之一:1.POSITIONAL_OR_KEYWORD定位参数和关键字参数传入的形参,2.VAR_POSITIONAL定位参数元组,3.VAR_POSITIONAL关键字参数字典4.KEYWORD_ONLY仅限关键字参数5.POSITIONAL_ONLY仅限定位参数(python声明函数暂时不支持,C实现的且不接受关键字参数的函数(如divmod)支持)

inspect.Signature对象还有bind方法,可以把任意参数绑定到签名的形参上,所用规则与实参到形参的匹配方式相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import inspect
>>> from test import tag
>>> sig = inspect.signature(tag)
>>> huchi_tag = {'name': 'img', 'title': 'huchi"s photo', 'src': 'huchi.jpg', 'cls': 'framed'}
>>> bound_args = sig.bind(**huchi_tag)
>>> bound_args
<BoundArguments (name='img', cls='framed', attrs={'src': 'huchi.jpg', 'title': 'huchi"s photo'})>
>>> for name, value in bound_args.arguments.items():
... print(name, '=', value)
...
name = img
cls = framed
attrs = {'src': 'huchi.jpg', 'title': 'huchi"s photo'}
>>> del huchi_tag['name'] #此时再调用inspect.bind()就会报错
>>> bound_args = sig.bind(**huchi_tag)
Traceback (most recent call last):...

函数注解

1
def clip(text:str, max_len:'int > 0'=80) -> str: pass

如此添加函数的注解。

对于注解不会做任何处理,只会存储在函数__annotations__(dict)属性中

1
2
3
>>> from test import clip
>>> clip.__annotations__
{'text': <class 'str'>, 'return': <class 'str'>, 'max_len': 'int > 0'}

这些东西不能派到什么实际的用场,在inspect.signature()函数中可以提取

1
2
3
4
5
6
>>> for param in sig.parameters.values():
... note = repr(param.annotation).ljust(13)
... print(note, ';', param.name, '=', param.default)
...
<class 'str'> ; text = <class 'inspect._empty'>
'int > 0' ; max_len = 80

支持函数式编程的包

operator

1
2
>>> def fact(n):
... return reduce(mul, range(1, n+1))

优美的给元组列表排序

1
2
3
from operator import itemgetter
a = [('huchi1', 2), ('huchi2', 3), ('huchi3', 1)]
a.sort(key = itemgetter(1))
1
2
for it in a:
print(itemgetter(1, 0)(it))

提取元组中的元素重新生成为一个元组。我们可以f = itemgetter(1, 0)然后再调用f函数。

其实itemgetter(1)的作用与lambda fields: fields[1]一样。

itemgetter使用[]运算符,因此他支持序列也支持映射和任何实现__getitem__方法的类。

attrgetter与itemgetter作用类似,创建的函数根据名称提取对象的属性。 比如key是元祖列表中的元祖中的元祖的元素就可以使用attrgetter.

functools.partial冻结参数

1
2
3
4
5
6
7
>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(4)
12
>>> [triple(i) for i in range(1, 10)]
[3, 6, 9, 12, 15, 18, 21, 24, 27]
|