前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python高级语法

Python高级语法

作者头像
故事尾音
发布2019-12-18 16:37:06
1.1K0
发布2019-12-18 16:37:06
举报

前言

本篇博客记录的是一些python的高级用法,更加深刻的理解Python的语法,。

基本操作进阶

各式各样的解析

问题:如何更高效的筛选数据?

命名元组

利用 collections 模块中的 namedtuple 函数

代码语言:javascript
复制
>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['name', 'age', 'sex', 'email'])
>>> s = Student('Jim', 21, 'male', '123@qq.com')
>>> s.name
'Jim'
>>> s.age
21

统计元素的频度

元组排序

元组是可以比较大小的,两个元组比较大小是依次比较,即如果第一个元素不相同,根据第一个元素的比较结果作为最终结果,否则比较第二个元素。

根据字典中的值排序

方法一:利用zip把字典转换为元组(值在前,键在后),然后sorted排序。

方法二:

利用sorted的key参数。字典的items返回由键值组成的元组列表(键在前,值在后),无法直接用sorted排序。我们可以用key指定使用列表里面每一个元组的第二个元素排序,这里使用lambda表达式。

多个字典的公共键

有序字典

历史记录功能

迭代器

迭代器

迭代(iteration)指的是去获取元素的一种方式,一个接一个。当你显式或隐式的使用循环来遍历某个元素集的时候,那就是迭代。

在Python里面,可迭代对象(iterable)和迭代器(iterator)有着特殊的含义。

  • iterable是实现了__iter__()方法的对象,该方法会返回一个iterator对象
  • iterator是实现了__iter__()__next__()方法的对象,__iter__()方法返回的是iterator对象本身

由此可见,iterableiterator的本质区别就是后者多了一个__next__()方法。 也就是说一个iterator对象必定是一个iterable对象。

当你使用一个for循环或者map,或着一个列表推导,那么会先通过iter()获取相应的迭代器, 然后每次循环自动通过next方法调用这个迭代器(iterator),从中获取每一个元素,从而完成迭代过程。

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

代码语言:javascript
复制
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

iteritem迭代大数据字典

迭代大数据字典时,如果是使用 items() 方法,那么在迭代之前,迭代器迭代前需要把数据完整地加载到内存,这种方式不仅处理非常慢而且浪费内存,下面代码约占1.6G内存

代码语言:javascript
复制
d = {i: i * 2 for i in xrange(10000000)}
for key, value in d.items():
    print("{0} = {1}".format(key, value))

而使用 iteritem() 方法替换 items() ,最终实现的效果一样,但是消耗的内存降低50%,为什么差距那么大呢?因为 items() 返回的是一个 list,list 在迭代的时候会预先把所有的元素加载到内存,而 iteritem() 返回的一个迭代器(iterators),迭代器在迭代的时候,迭代元素逐个的生成。

代码语言:javascript
复制
d = {i: i * 2 for i in xrange(10000000)}
for key, value in d.iteritem():
    print("{0} = {1}".format(key, value))

生成器

当我们调用一个普通的函数时,执行过程从第一条语句开始,直到碰到一个return语句或者遇到一个异常抛出, 再或者到了函数最后一条语句(实际上相对于一个隐式的return None)的时候结束。 一旦这个函数返回后将控制权交还给它的调用者,它里面所有的局部变量值都消失了,当你重新调用它的时候,一切又将重新开始。

这就是我们通常意义上面所认识的函数(或者说是子程序),但有时候我们需要创建某个函数,它并不简单的返回一个值, 而是可以不断的释放一个值的序列。那么这个特殊的函数就需要能够“保存”它的状态。

在Python中,有这种能力的“函数”被称为生成器,它们相当有用。生成器(yield语句)刚开始被引入进来主要是用来方便的生成序列值。

迭代器切片操作

迭代器迭代多个对象

字符串

拆分多种分隔符的字符串

slots节省内存

Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class实例能添加的属性

代码语言:javascript
复制
class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

使用@property

python最佳编程实践推荐我们不要像java那样去调用getter和setter,而是使用装饰器@property

Python内置的@property装饰器就是负责把一个方法变成属性调用

把一个getter方法变成属性,只需要加上@property就可以了, 此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

代码语言:javascript
复制
class Student(object):
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
s = Student()
s.score = 60
s.score

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性

函数装饰器

斐波那契数列

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

下面是一个简单的求斐波那契数列的算法,用的递归,很简单:

代码语言:javascript
复制
def fibonacci(n):
    if n <= 1:
        return 1
    else:
        return  fibonacci(n - 1) + fibonacci(n - 2)

Memoization技术

但是执行fibonacci(50)却需要花费很多时间。这是因为递归求解的时候计算了很多重复子序列。我们可以把求解斐波那契看成是一个二叉树,如下图所示:

一种想法是以空间换时间,计算一次,然后存储,下次需要的时候直接取出,不需要redo前面的计算,称之为Memoization 技术。加入缓存后的程序如下:

代码语言:javascript
复制
def fibonacci(n, chche=None):
    if chche is None:
        chche = {}

    if n in chche.keys():
        return chche[n]

    if n <= 1:
        return 1
    else:
        chche[n] = fibonacci(n - 1, chche) + fibonacci(n - 2, chche)

    return chche[n]

加入装饰器

这下就执行的非常快了。但是还有一个问题,就是加入我们有很多这样的函数,每个这样写很麻烦,这就引入了装饰器。加入装饰器后的代码如下:

代码语言:javascript
复制
def memo(func):
    cache = {}
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

@memo
def fibonacci(n):

    if n <= 1:
        return 1
    else:
        return  fibonacci(n - 1) + fibonacci(n - 2)


if __name__ == '__main__':
    print(fibonacci(50))

但是这个时候如果我们调用下面的这句代码:

代码语言:javascript
复制
print(fibonacci.__name__)
#wrap

更新元数据

看来原函数的元数据被装饰器改变了。解决方法就是使用functools中的装饰器wraps装饰内部包裹函数,可以吧原函数的属性更新到包裹函数上。

代码语言:javascript
复制
from functools import update_wrapper

def memo(func):
    cache = {}
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    update_wrapper(wrap,func)
    return wrap

主要代码是:

代码语言:javascript
复制
from functools import update_wrapper
update_wrapper(wrap,func)

还有一种更简便的写法,和上面的代码效果相同,就是用装饰器去装饰我们写的装饰器:

代码语言:javascript
复制
from functools import wraps

def memo(func):
    cache = {}
    @wraps(func)
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

带参数的装饰器

下面实现的是一个参数类型检查器。

代码语言:javascript
复制
from functools import wraps

from inspect import signature

def typeassert(*ty_args,**ty_kargs):
    def decorator(func):
        sig = signature(func)
        btypes =  sig.bind_partial(*ty_args,**ty_kargs).arguments
        def wrap(*args,**kwargs):
            for name,obj in sig.bind(*args,**kwargs).arguments.items():
                if name in btypes:
                    if not isinstance(obj,btypes[name]):
                        raise  TypeError('"%s" must be "%s"' %(name,btypes[name]))
            return func(*args,**kwargs)
        return wrap
    return decorator

def memo(func):
    cache = {}
    @wraps(func)
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

@typeassert(int)
@memo
def fibonacci(n):

    if n <= 1:
        return 1
    else:
        return  fibonacci(n - 1) + fibonacci(n - 2)


if __name__ == '__main__':
    print(fibonacci('a'))
    
#Traceback (most recent call last):
#  File "/home/sunyan/PycharmProjects/sunyan/sunyan.py", line 37, in <module>
#    print(fibonacci('a'))
#  File "/home/sunyan/PycharmProjects/sunyan/sunyan.py", line 12, in wrap
#    raise  TypeError('"%s" must be "%s"' %(name,btypes[name]))
#TypeError: "n" must be "<class 'int'>"

这里我们输入了字符型变量,但是要求输入是一个int变量,所以程序报错,提示应该输入int类型。

属性可修改的函数装饰器

下面是一个测试函数运行时间的装饰器,并记录进入日志里面。

代码语言:javascript
复制
import time
import logging
logging.basicConfig(level=logging.INFO)
def runtime(func):
    @wraps(func)
    def wrap(*args, **kwargs):
        start = time.time()
        used = time.time()-start
        msg = '%s used time: %s' %(func.__name__,used)
        logging.info(msg)
    return wrap

总体程序

代码语言:javascript
复制
from functools import wraps

from inspect import signature

def typeassert(*ty_args,**ty_kargs):
    def decorator(func):
        sig = signature(func)
        btypes =  sig.bind_partial(*ty_args,**ty_kargs).arguments
        @wraps(func)
        def wrap(*args,**kwargs):
            for name,obj in sig.bind(*args,**kwargs).arguments.items():
                if name in btypes:
                    if not isinstance(obj,btypes[name]):
                        raise  TypeError('"%s" must be "%s"' %(name,btypes[name]))
            return func(*args,**kwargs)
        return wrap
    return decorator

import time
import logging
logging.basicConfig(level=logging.INFO)
def runtime(func):
    @wraps(func)
    def wrap(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        used = time.time()-start
        msg = '%s used time: %s' %(func.__name__,used)
        logging.info(msg)
        return res
    return wrap


def memo(func):
    cache = {}
    @wraps(func)
    def wrap(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrap

@runtime
@typeassert(int)
@memo
def fibonacci(n):

    if n <= 1:
        return 1
    else:
        return  fibonacci(n - 1) + fibonacci(n - 2)


if __name__ == '__main__':
    print(fibonacci(500))

正则表达式

正则表达式

Reference

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 基本操作进阶
    • 各式各样的解析
      • 命名元组
        • 统计元素的频度
          • 元组排序
            • 根据字典中的值排序
              • 多个字典的公共键
                • 有序字典
                  • 历史记录功能
                  • 迭代器
                    • 迭代器
                      • iteritem迭代大数据字典
                      • 生成器
                        • 迭代器切片操作
                          • 迭代器迭代多个对象
                          • 字符串
                            • 拆分多种分隔符的字符串
                              • slots节省内存
                                • 使用@property
                                • 函数装饰器
                                  • 斐波那契数列
                                    • Memoization技术
                                      • 加入装饰器
                                        • 更新元数据
                                          • 带参数的装饰器
                                            • 属性可修改的函数装饰器
                                              • 总体程序
                                              • 正则表达式
                                              • Reference
                                              领券
                                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档