快乐学习
前程无忧、中华英才非你莫属!

Python 两大凶器-迭代器与生成器

zhangtongle阅读(1721)

1、前言

迭代器与生成器在Python 中非常有用的工具,可以帮助我们处理复杂数据问题的同时也能让我们的代码更加整洁性能更好。


2、迭代器概念

英文名:iterator,实现了方法 iternext 的对象是迭代器。

python 中的for 在底层就实现了迭代器这种技术。所以我们可以使用统一的for xxx in xxx: 的这种固定格式来迭代我们的数据。

例子:

for element in [1, 2, 3]:
    print(element)
for element in (1, 2, 3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in "123":
    print(char)
for line in open("myfile.txt"):
    print(line, end='')

在代码的底层,for 语句会在容器对象上调用 iter()函数,该函数返回一个定义了 next() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,next() 将引发 StopIteration 异常来通知终止 for 循环。 你可以使用 next() 内置函数来调用 next() 方法;这个例子显示了它的运作方式:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

3、创建迭代器

3.1 官方案例:

看过迭代器协议的幕后机制,给你的类添加迭代器行为就很容易了。 定义一个 iter() 方法来返回一个带有 next() 方法的对象。 如果类已定义了 next(),则 iter() 可以简单地返回 self:

class Reverse:
    """用于向后循环序列的迭代器."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s

3.2 自定义案例:

class MultiplyByTwo:
    def __init__(self, number):
        self.number = number
        self.counter = 0

    def __next__(self):
        self.counter += 1
        return self.counter, self.number * self.counter

mul = MultiplyByTwo(200)
print(next(mul))
print(next(mul))
print(next(mul))

'''
输出结果
(1, 200)
(2, 400)
(3, 600)
'''

让我们看看在Python 中迭代器是如何工作的,上面的代码,我们实现了一个MultiplyByTwo的类,该类有一个名字为next 函数,每当它调用时。都会返回一个新的迭代器,迭代器内部通过维护一个变量来记录序列中的位置,我们可以看到next 方法中使用了counter变量。但是我们使用for循环遍历这它就会出现异常:

for i in mul:
    print(i)

# TypeError: 'MultiplyByTwo' object is not iterable

类型错误,MultiplyByTwo对象是不可迭代。这里强调,MultiplyByTwo它是一个迭代器,但是它不是一个可迭代的对象,为了能让他可以通过for循环遍历,需要把它变成可迭代对象,需要在类里面添加iter方法。

例如:


class MultiplyByTwo:
    def __init__(self, number):
        self.number = number
        self.counter = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1
        return self.counter, self.number * self.counter

for i in mul:
    print(i)

'''
输出结果:会一直循环遍历下去成死循环

(1, 200)
(2, 400)
(3, 600)
此处为省略
(12368, 2473600)
此处为省略
'''

虽然我们的MultiplyByTwo迭代器,可以让for循环遍历了,但是它成死循环了,不是我们想要的,所以我们需要继续优化它。

例如:

class MultiplyByTwo:
    def __init__(self, number, bigValue):
        self.number = number
        self.counter = 0
        self.bigValue = bigValue

    def __iter__(self):
        return self

    def __next__(self):
        self.counter += 1

        value = self.number * self.counter

        if value > self.bigValue:
            raise StopIteration
        else:
            return self.counter, value

mul = MultiplyByTwo(200, 1000)

for i in mul:
    print(i)

'''
输出结果为:返回有限数据的迭代器。
(1, 200)
(2, 400)
(3, 600)
(4, 800)
(5, 1000)
'''

当引发StopIteration 这个异常时,MultiplyByTwo对象收到已经达到限制的信号,for 循环会自动处理这个异常,并退出循环。


4、生成器概念

在 Python 中,使用了 yield 的函数被称为生成器(generator),它又叫做生成迭代器的工具

生成器 是一个用于创建迭代器的简单而强大的工具。 当它要返回数据时会使用 yield 语句。每次在生成器上调用 next() 时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。

生成器在读取大量数据或者大量文件时,非常有用,他可以被暂停和恢复。生成器返回一个像列表一样可被迭代的对象。

这里做重要的概念区分:

可迭代对象:

列表、字典、元组、字符串、文件对象、迭代器 他们都是可迭代对象
因为它们内部都包含iter函数。

列表、字典、元组、字符串、文件对象他们都不属于迭代器,因为他们并不包含 next函数。

看到这里有些小伙伴蒙圈了,如何区分呢:

第一种方式:

可以使用pycharm 快捷键,ctrtl+ 鼠标单击对应list、dict 、类对象来查看类内部是否包含iter和next 函数,来区分是迭代器,还是普通的可迭代对象。在这里我们可以把迭代器看成一种特殊的可迭代对象。

第二种方式

from collections.abc import Iterator # 迭代器
from collections.abc import Iterable # 可迭代对象

# rev 变量为3.1 案例中rev变量。
isinstance(rev, Iterator) # True 输出真 说明他是迭代器
isinstance(rev, Iterable) # True 输出真,说明它又是可迭代对象

isinstance([1, 2, 3], Iterable) # Ture ,他是可迭代对象
isinstance([1, 2, 3], Iterator) # False ,说明他不是迭代器。

# 我们通过第二种方式,来判断它到底是不是迭代器的一个好办法。

5、官方生成器的案例

可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。 但生成器的写法更为紧凑,因为它会自动创建 iter() 和 next() 方法。

另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。 这使得该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。

除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。

def reverse(data):
    print('开始')
    for index in range(len(data) - 1, -1, -1):
        print('for开始')

        yield data[index]  # 调用next停止的地方 ,

        print('for结束')  # 生成器从它停止的地方恢复

    print('结束')

rev = reverse('golf')
next(rev)

这里不断调用next 来输出对应的值,小编在例子中加了打印语句,可以方便大家可以看到在哪里暂停,又是在哪里恢复。

我们在对rev 进行判断,看看它到底是什么类型:


rev = reverse('golf')
from collections.abc import Iterator  # 迭代器

isinstance(rev, Iterator)  # True

总结:生成器的最终概念,生成迭代器。想要用好yield 你可以把它当做return 使用即可,但是它不会停止程序哦,并且要记住当再次遍历它时,它会从上次离开的位置恢复执行(并且会记住上次执行语句时的所有数据值),


6、生成器表达式

某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,但外层为圆括号而非方括号。 这种表达式被设计用于生成器将立即被外层函数所使用的情况。 生成器表达式相比完整的生成器更紧凑但较不灵活,相比等效的列表推导式则更为节省内存。

官方案例:

>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260

>>> unique_words = set(word for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']

7、生成器案例

当我们想要生成一个0.5 到6.5 步长为0.2 的序列,使用range函数会报错,因为它不支持浮点数:

range(0.5, 6.5, 0.2)
# TypeError: 'float' object cannot be interpreted as an integer

那我们使用生成器技术,来优化range函数,自定义一个来符合我们的要求

# 生产某个范围内浮点数的生成器:
def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x
        x += increment

fr = frange(0.5,6.5,0.2)

next(fr)
0.5
next(fr)
0.7

读取linux 系统比较大的数据文件,进行范围查询时,可使用切片迭代器技术,来加速查询:

from itertools import islice #

f = open('/var/log/dmesg')
for line in islice(f, 100, 300):  # 截取100行到300行之间的内容
    print(line)
for line in islice(f, 500):  # 截取前500行之间的内容
    print(line)
for line in islice(f, 100, None):  # 截取100行到文件末尾之间的内容
    print(line)

可以让大家轻松写成可读性较高的python 点击这个连接可以查看itertools 模块来安排迭代器之间的交互关系。


8、何时使用迭代器

在处理大规模数据文件时或数据流(在线视频)时非常有用,迭代器可以让我们更加灵活地一次只处理一段数据,而不是必须一次性的将全部数都加载到内存当中。

函数不要直接返回列表,应该让他逐个生成列表里面的值,用生成器来实现比让函数把结果存储到列表里在返回更清晰一些。

生成器函数所返回的迭代器产生的一系列值,都是由函数体的下一条yield 表达式决定的。


总结:这一篇章,最重要的技术是迭代器,使用迭代器技术,才能性能更好,使用生成器才会让我们的代码更加简洁

Python中模块与包的运用

zhangtongle阅读(2480)

1、import介绍

它是用来导入工具包提供给我们可以快速构建我们的程序的功能函数。

> Correct:
import os
import sys

> Correct:
from subprocess import Popen, PIPE

导入应按以下顺序分组:

  1. 标准库导入。
  2. 相关第三方。
  3. 本地应用程序/库特定的导入,您应该在每组导入之间放置一个白行。
  4. 推荐绝对导入
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

5、相对导入(不推荐)python3 中已经废除

from . import sibling
from .sibling import example

2、导入模块的正确姿势

模块只是扩展名为.py 的文件,模块的名称就是文件的名称。模块当中可以有很多函数或类。

当我们创建模块名的时候,尽量要言简意赅。

不要这样:import create_women_object
应该这样:import girlfriend  或者from users import girlfriend
不要这样: import users.women.girlfriend、import USERS
应该这样: import users_girlfriend、import users

避免使用在文件名中使用点和特殊字符,导入代码的可读性也至关重要,毕竟万事开头难。开头的阅读新也至关重要。

不要这样:from user import *
         friend = get_girlFriend()

应该这样:from user import get_girlFriend
         friend = get_girlFriend()

最好是这样:import user
           user.get_girlFriend() # 可以明确标记get_girlFriend() 哪个模块下的。

当巧合出现: from user import get_girlFriend
            from person import get_girlFriend

            friend1 = get_girlFriend() # 就会发生冲突
            friend2 = get_girlFriend() # 就会发生冲突

冲突:如果两个包里有同名模块,或者两个模块里有同名的函数或类,那么后引入的那个会把先引入的覆盖掉。

解决冲突使用as起别名:  from user import get_girlFriend as u_gf
                      from person import get_girlFriend as p_gf

as 不仅可以给函数换名字,防止冲突,还能给整个模块换个名称,例如:import pandas as pd 

这是数据分析核心模块的pandas 的常规引入写法。如果pandas 还没有掌握,赶紧去查看同乐老师的[python自动化办公的10、11、16章的内容](https://edu.51cto.com/course/21337.html "python自动化办公的10、11、16章的内容")。

3、init.py 是干啥的、包干哈的?

自从Python3.3之后,不需要在目录中包含init.py文件来表明这个目录是一个Python 包。
3.3之前是需要的,Pycharm 在建立package的时候会默认新建一个,这个init尽管新版本不需要它来表示我是一个包了,但是它还有许多妙用。最好是不要删除,以便引起不必要的麻烦和异常。

包的含义简单解释:是用来管理模块的,就是一个文件夹下面可以创建很多模块。

来源于Python官方社区

包是一种用“点式模块名”构造 Python 模块命名空间的方法。例如,模块名 A.B 表示包 A 中名为 B 的子模块。导入包时,Python 搜索 sys.path 里的目录,查找包的子目录。

Python 只把含 init.py 文件的目录当成包。这样可以防止以 string 等通用名称命名的目录,无意中屏蔽出现在后方模块搜索路径中的有效模块。 最简情况下,init.py 只是一个空文件,但该文件也可以执行包的初始化代码,或设置 all 变量。

购物车模块:

利用init对购物车模块进行拆解

模块拆解的好处,就是好维护,功能单一职责,每个模块负责对应相关的功能职责。

利用init对拆解的模块逻辑式黏连在一起

但是我们再把拆开的模块,逻辑式的黏连在一起对外提供使用,就可以利用init.py

我们在modle_package包下的init.py文件中以from 的形式进行黏连在一起,以modle_package 作为对外服务的公共接口。来达到分久必合的原理。这种方式会让你用一个模块来处理项目的不同功能,这在大型项目和第三方库中特别有用。但是会给客户端要弄清楚这个功能函数在哪一个模块下,带来了一些负担,特别是喜欢用相对导入的小伙伴,利与弊自己权衡,我还是那句话,适合自己的才是最好的,PE8也不是都适合我们。

PE8代码规范:标准库代码应避免复杂的包布局并始终使用绝对导入。
在__init__文件中使用
from . import echo
from .. import formats
from ..filters import equalizer 

# 这种叫做相对导入,大多数情况下不建议这么做,这么做得唯一好处就是,可以省略冗长的导入,以及避免外部包的结构或者包名而改动。**

4、通过包构建稳定的API,使用all关键字

API 就是你写好的功能函数,以开源软件包的形式,提供给别人使用。以免新旧版本差异过大,你就得把他的功能稳定下来,为了做到这一点,就得隐藏软件包内的代码结构。不要让外部开发者依赖这套结构,

Python 允许我们通过all这个特殊属性,决定模块或包里面有哪些内容应该当做API公布给外界。all的值是一份列表,一般from foo import * 与 all 才是最佳拍档。

下面案例把相互关联的模块,以API方式共享

这种做法,可以很好的帮助我们打造明确而稳固的api.如果说你不对外提供,其实没有必要这么麻烦。

并且 from model_package import * 在内部用时,是禁止推荐使用的。


5、有用的扩展

有用扩展来源于python cookbook

读取位于包中的数据文件

mypackage/
    __init__.py
    somedata.dat
    spam.py

现在假设spam.py文件需要读取somedata.dat文件中的内容。你可以用以下代码来完成:

# spam.py
import pkgutil
data = pkgutil.get_data(__package__, 'somedata.dat')

pkgutil是Python自带的用于包管理相关操作的库,pkgutil能根据包名找到包里面的数据文件,然后读取为bytes型的数据。如果数据文件内容是字符串,那么直接decode()以后就是正文内容了。

为什么pkgutil读取的数据文件是bytes型的内容而不直接是字符串类型?
这是因为并不是所有数据文件都是字符串,如果某些数据文件是二进制文件或者图片,那么以字符串方式打开就会导致报错。所以为了通用,pkgutil会以bytes型方式读入数据,这相当于open函数的“rb”读取方式。

使用pkgutil还有一个好处,就是只要知道包名就可以找到对应包下面的数据文件,数据文件并不一定要在当前包里面。!

下面案例截图来源于互联网:
https://www.cnblogs.com/du-jun/p/12192797.html


6、简单快捷导入多层级包下的某个模块

可以直接把多层级下某个模块,导入到系统环境变量,就像这样、

官方的解释
变量 sys.path 是字符串列表,用于确定解释器的模块搜索路径。该变量以环境变量 PYTHONPATH 提取的默认路径进行初始化,如未设置 PYTHONPATH,则使用内置的默认路径。可以用标准列表操作修改该变量:

import sys
sys.path.append('/ufs/guido/lib/python')

牛奶喝多了,有点醉了,深夜写写博客缓解一下,写了快一个多小时了,不写了,困了,睡觉!
明天继续更~

Python进阶编程必备技能-装饰器

zhangtongle阅读(3055)

1、装饰器的由来

想扩展自己的函数功能,又不想动自己原先的函数代码逻辑,那就得用装饰器,在其他语言里例如java 叫做面向切面编程,老师会给我们讲一堆面向切面底层实现的原理的过程中,我相信你已经丧失了对Java学习的兴趣,伤害永远是对比出来的,哈哈~。

在这里我会全面简单的介绍一下,装饰器有几种模式,不强调你使用哪种模式,按你的需求来。

2、初级自定义装饰器

func为函数名称,这里的思想是把函数作为参数传递给固定某一个函数,这种属于把函数转换为参数的思想。目的有很多种,比较实用的目的是让我们的各种功能行为更加符合规范.举例:比如我们之前有吃早饭,吃晚饭,吃中午饭函数,函数太多记不住,我们就定义了一个吃饭的函数,把吃晚饭、吃中午饭、吃早饭降维为参数进行传递和调用。

def say_Chinese():
    return "我是say_Chinese"

def say_English():
    return "我是say_English"

def say(func):
    return func()

say(say_Chinese)

3、中级自定义装饰器

初级装饰器其实已经满足了我们的需求,为什么要学中级装饰器呢,我们通过wrapFunction 来更改你传进来func 函数的前后的逻辑行为,say_middle 你可以定义多个wrapFunction1,2 来满足各种行为逻辑,并且根据判断条件return 返回对应的装饰器函数即可。

def say_middle(func):
    # 装饰函数
    def wrapFunction():
        print("说话之前,准备演讲稿")  #
        say_style = func()
        print("说话完毕,进行总结")

        return say_style

    return wrapFunction

@say_middle
def say_People():
    return "我是say_People"

# 因为say_People 头顶上装饰了say_middle
# 在调用say_People时候对say_People自动进行装饰。
# @say_middle 是最常用的。
say_People()

当我们想要知道当前执行的是哪个被装饰过得名称的时候。

print(say_People.__name__) # 打印的却是wrapFunction
# 为了解决优化这个问题,下面改进下我们的装饰器

4、改进装饰器

# 为不改变被装饰函数或类的性质,添加functools.wrap装饰器
from functools import wraps
def say_high(func):
    @wraps(func)
    def wrapFunction():
        print("说话之前,准备演讲稿")  
        say_style = func()
        print("说话完毕,进行总结")
        return say_style

    return wrapFunction

5、在类中创建装饰器

有的时候我们的装饰器更加独立化,类和模块都可以用来创建装饰器,Python对于创建装饰器没有限制,这完全取决于你。

为啥类能当装饰器,是因为Python提供了一个特殊的方法call 意味着类的实例可以当做函数来调用。

class Say_Super:

    def __call__(self, func):
        @wraps(func)
        def wrapFunction():
            print("说话之前,准备演讲稿")  #
            say_style = func()
            print("说话完毕,进行总结")
            return say_style

        return wrapFunction

@Say_Super()
def say_Code():
    return "我是say_Code"

say_Code()

6、带参数的装饰器

我们之前定义了都是不带参数的函数,其实不带参数的函数,say_Chinese、say_People的,都是没有灵魂的,因为这些函数,只能处理写成固定逻辑的功能,带参数就会让我们的功能更强,更灵活,因为有了值得判断和比较就会执行不同逻辑的代码。*args, **kwargs 一个是关键字参数,一个是命名关键字参数,目的是让函数可以灵活的传递各类型的参数。如果这块薄弱了,赶紧看看同乐老师录制的Python自动化办公的第十五章基础增强篇,进行基础增强,当基础增强了,才能更能快速掌握Python 的进阶编程所需要的内容。

定义一个装饰器把返回的字符串改成大写。

def to_uppercase(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        text = func(*args, **kwargs)
        if not isinstance(text, str):
            raise TypeError('你传的参数不是字符串,请重新输入')
        return text.upper()
    return wrapper

@to_uppercase
def say_Language(say_str):
    return say_str

language = say_Language('speak chinese')
print(language)  # 输出SPEAK CHINESE

7、在类的方法上使用装饰器

# 可以自定义重复请求的次数,和间隔的秒速
def retry_requests(retries=3, delay=5):
    def try_request(fun):
        @wraps(fun)
        def retry_decorators(*args, **kwargs):
            for _ in range(retries):
                res = fun(*args, **kwargs)
                print(res)
                time.sleep(delay)

        return retry_decorators

    return try_request

class ApiRequest:
    def __init__(self, url, headers):
        self.url = url
        self.headers = headers

    @retry_requests(retries=5, delay=1)
    def create_request(self):
        res = requests.get(url=self.url, headers=self.headers)

        return res

aq = ApiRequest('https://www.ztloo.com', headers=None)
aq.create_request()

8、解除装饰器函数

通过wrapped 关键字解除

@to_uppercase
def say_Language(say_str):
    return say_str

new_say_Language = say_Language.__wrapped__
language = new_say_Language('speak chinese')
print(language)  # 输出SPEAK CHINESE

当我们知道装饰器是咋回事的时候,其他的就需要积累,可以看看开源的模块中,的装饰器是如何管理,和实现的,让我们后续用最少的代码,实现功能最丰富的功能。

我是同乐,我为Python 自动化带盐!!!!!!!!!!!!!!!!!!

自动生成Python 文档-Sphinx

zhangtongle阅读(3308)

1、安装sphinx 模块

pip install Sphinx

2、创建doc文档项目路径

PS D:\rd\PyJob\tongle> sphinx-quickstart pydoc
Welcome to the Sphinx 4.2.0 quickstart utility.

> Separate source and build directories (y/n) [n]: y

The project name will occur in several places in the built documentation.
> Project name: ztloo
> Author name(s): 张同乐
> Project release []: 1.0
> Project language [en]: zh_CN

3、配置conf.py

import os
import sys
sys.path.insert(0, os.path.abspath('../../clean'))

添加扩展,来识别各种格式的注释

extensions = ['sphinx.ext.autodoc',
              'sphinx.ext.doctest',
              'sphinx.ext.intersphinx',
              'sphinx.ext.todo',
              'sphinx.ext.coverage',
              'sphinx.ext.mathjax',
              'sphinx.ext.napoleon']

4、生成RST文件

PS D:\rd\PyJob\tongle\pydoc> sphinx-apidoc -o ./source ../clean/
Creating file ./source\coding_clear.rst.
Creating file ./source\ztloo.rst.
Creating file ./source\modules.rst.

5、生成html

PS D:\rd\PyJob\tongle\pydoc> ./make html
Running Sphinx v4.2.0
loading translations [zh_CN]... done
making output directory... done

Python-Dash开发交互式数据大屏-介绍与安装-1

zhangtongle阅读(1824)

Dash介绍

Dash 每月下载 600,000 次,是原始的低代码框架,用于在 Python、R、Julia、F# 和 MATLAB® 中快速构建数据应用程序。

Dash 编写在 Plotly.js 和 React.js 之上,非常适合在纯 Python、R、Julia、F# 或 MATLAB® 中构建和部署具有自定义用户界面的数据应用程序。它特别适合处理数据的任何人。

通过几个简单的模式,Dash 抽象出了构建具有交互式数据可视化功能的全栈 Web 应用程序所需的所有技术和协议。

Dash 非常简单,您可以在 10 分钟内将用户界面绑定到您的 Python、R、Julia、F# 或 MATLAB® 代码。

Dash 应用程序在 Web 浏览器中呈现。您可以将应用程序部署到 VM 或 Kubernetes 集群,然后通过 URL 共享它们。由于 Dash 应用程序是在 Web 浏览器中查看的,因此 Dash 本质上是跨平台和移动就绪的。

框架背后有很多东西。要了解有关它是如何构建的以及 Dash 的动机的更多信息,请阅读我们的公告信或我们的帖子 Dash is React for Python。

Dash 是一个在 MIT 许可下发布的开源库。 Plotly 开发了 Dash,还提供了一个平台,用于在企业环境中编写和部署 Dash 应用程序。如果您有兴趣,请联系。

这些文档是在 Azure Kubernetes 服务上的 Dash Enterprise 上运行的 Dash 应用程序。

在 Dash Enterprise Kubernetes 集群上编写、部署和扩展 Dash 应用程序。

[========]

Dash 教程

step1 安装

pip install dash

这也带来了plotly图形库。该库正在积极开发中,因此请经常安装和升级。

如果您更喜欢Jupyter notebook 或 JupyterLab 作为您的开发环境,我们建议您安装jupyter-dash:

pip install jupyter-dash

这些文档正在运行dash版本2.0.0.

此版本支持 Python 3。dash之前的版本2.0.0也支持 Python 2。我们还建议安装Pandas,这是Plotly Express所必需的,并在我们的许多示例中使用。

pip install pandas

准备好?现在,让我们制作您的第一个 Dash 应用程序。

体验一把Python新版本是什么感受!

zhangtongle阅读(1897)

Python升级介绍

  • 前言小故事

    小编的笔记本电脑已经用了很多年,至今估计有5年都没有重装过系统,C盘空间小的可怜,经常在网上下载各种带有后门的小工具,一是为了使用,二是为了研究带后门(病毒)的这些程序有多强大,有的强大的会让360把感染的系统核心文件给删除掉,年复一年日复一日,系统是各种问题,强忍着一直没重装系统,最后出现的问题忍不了了,C盘无优化空间,任务管理器打不开、桌面经常黑屏,

重装系统小技巧:找个大点的移动硬盘,把重要数据文件压缩成包,移动到移动硬盘里面、然后重装系统、为了防止系统再次被感染,4个C、D、E、F 快速格式化,然后找个纯净系统,虽然很讨厌360,卫视加杀毒,急救箱,对刚装的系统进行2轮的深度扫描才放心、因为放到移动硬盘里面的文件肯定有被感染的,那就用360监控起来,不让移动硬盘里的文件有执行权限。

  • python 全面升级

之前前几年一直使用的Python3.7.0 和Pycharm 2017 版本。为了体验一下新版本也对应进行升级。

升级为Python 3.8.10 和Pycharm 最新版本。

注意:win7系统只能安装python3.8 系列,win10才能 享受3.9以上版本,最近发现Python 升级频繁。已经到3.11系列。

    不建议去追赶,因为很多第三方库和相关联的模块它跟不上,一旦你使用了最新版本,之前实现的功能,大量依赖的第三方模块就完犊子了,用不了了。so,python3.7 还是中坚力量。

python3.8 命令控制台显示效果

pycharm新版展示效果

  • 小编人目前体验到的额,pycharm 新版本重大改进 ,

可以创建独立的项目环境
协同开发:Code With Me
ALT+ENTER 快捷键、直接更正你的Python代码语法规范、代码格式化问题
代码提示,增强了一个量级,之前有些api 不提示,现在也提示了,并且提示的非常好。
函数的建议更加准确,就是参数错误或不传参数,提示类型只是警告。让我调成了错误。****

通过ALTER+ENTE快捷键整理的代码如下:

Python3.8 新的语法,海象表达式

目的:让你的代码又少一行。
缺点:牺牲了代码的百分之10的可读性。

没使用海象表达式的code 代码:

# Before 这样式的
def do_size():
    list_nums = [1, 5, 7, 2, 6, 7, 8]  # 数量为7
    nums = len(list_nums)
    if nums > 5:
        print(f'大于5,实际数量为{nums}')

使用海象表达式code 代码:

# Now 这样式的,叫海象表达式,因为 :=  中冒号像海象的眼睛,等于号像海象的两个牙齿。
def do_change():
    list_nums = [1, 5, 7, 2, 6, 7, 8]  # 数量为7
    # nums = len(list_nums) #  节省这一步
    if (nums := len(list_nums)) > 5:
        print(f'大于5,实际数量为{nums}')

使用海象表达式还能结合while循环 code 代码:


def do_while():
    list_nums = [1, 5, 7, 2, 6, 7, 8]

    # while 循环的时候,一边获取值,一边判断,吼吼还是很强大
    while (block := list_nums.pop()) > 5:
        print(block)

太懒了,直接copy 官方的demo 了。

discount = 0.0
if (mo := re.search(r'(\d+)% discount', advertisement)):
    discount = float(mo.group(1)) / 100.0

在正则表达式匹配期间会出现类似的好处,其中需要两次匹配对象,一次用于测试是否发生匹配,另一次用于提取子组。

有人呼叫我,暂时写到这~~

原创-史上最强Numpy 学习博客

zhangtongle阅读(2283)

前言

小编之前录制的Python自动化办公课程里面没有录制Numpy 的内容,是因为有多维数组的存在,对于萌新来说,三维以上的数组,数据查询和计算,多多少少会有点头大。

Python 数据分析达人四剑客: numpy pandas sklearn 在加个平民版的Openpyxl 。
如果你想专业从事数据分析,外加个SQL技能。

pandas 你可以理解为它集成了 Numpy的查询和计算功能,以及Openpyxl 处理Excel的功能。

pandas是基于numpy数组构建的,但二者最大的不同是pandas是专门为处理表格和混杂数据设计的,比较契合统计分析中的表结构,而numpy更适合处理统一的数值数组数据。

大多数场景我们用Pandas 其实可以很好地完成我们的任务,但是Pandas ,它并没有完完全全的融合Numpy和Openpyxl.

在一些数据场景下,反而直接用Openpyxl 和Numpy处理起来更加快捷和方便。

numpy pandas or Openpyxl ,在处理复杂的数据情况下,我建议是可以混着用。

Numpy 学好,确实可以对Pandas的使用,更上一层楼。

这里,整合了一些于Numpy常用的知识点

  • 1、创建ndarray数组
  • 2、索引查询(单索引、切片、funcy index、联合索引)
  • 3、通用函数(加减乘除取余次幂)
  • 4、聚合函数(sum、mean、max、min、any、all、中位数、排名等)
  • 5、广播规则
  • 6、条件比较,布尔索引取值
  • 7、排序

下面有将近2千行的Numpy 学习博客,让你一次学个够!

参考来源:Python数据科学手册

1、初识多维数组


import numpy as np
'''核心'''

'''
# 三维
# axis=0 :Y轴方向(竖着)以组为单位。
# axis=1 :Z轴方向竖着比较,以组中的上下行 。
# axis=2 :x轴方向横着比较。

# 二维
# axis=0 :Y轴方向(竖着)
# axis=1 :x轴方向(横这)

# 索引
# 一维
# x[start:stop:step]

# 二维
# x[Y,X] #Y轴索引,X轴索引
# x[Y1:Y2,X1:X2] # 索引切片范围查询
# x[Y1:Y2:step,X1:X2:step] # 索引切片范围查询,加索引步长

# 三维
# x[Y:Z:X] ,Y轴、 Z轴是每块(每组)的上下行索引,X轴
# x[Y1:Y2, Z1:Z2,X1:X2]

2、创建数组

#################1、创建数组##################################
np.random.seed(0) # seed for reproducibility

x1 = np.random.randint(10, size=6) # One-dimensional array
x2 = np.random.randint(10, size=(3, 4)) # Two-dimensional array
x3 = np.random.randint(10, size=(3, 4, 5)) # Three-dimensional array

'''
每个数组都有属性ndim(维数),
shape(每个维的大小)和size(数组的总大小):
'''
# print("x3 ndim: ", x3.ndim)
# print("x3 shape:", x3.shape)
# print("x3 size: ", x3.size)

# print("dtype:", x3.dtype) # 另一个有用的属性是dtype数组的数据类型

'''
其他属性包括itemsize,列出每个数组元素的大小(以字节为单位),
以及nbytes列出数组的总大小(以字节为单位):
一般而言,我们期望它nbytes等于itemsizeTimes size。
'''
# print("itemsize:", x3.itemsize, "bytes")
# print("nbytes:", x3.nbytes, "bytes")

3、利用索引访问单一元素

# 二维数组
# print(x2)

# 二维数组访问
# print(x2[0,0])
# print(x2[2,0])
# print(x2[2,-1])

# 二维数组赋值
# x2[0,0] = 12
# print(x2)

'''
请记住,与Python列表不同,NumPy数组具有固定类型。
例如,这意味着,如果您尝试将浮点值插入整数数组,则该值将被静默截断。
不要被这种行为所困扰!
'''
# x1 [ 0 ] = 3.14159 #这将被截断!
# print(x1)

4、数组切片

# 一维
# x[start:stop:step]
# x[::2] # 1,3,5,7
# x[1::2]
# x[:8:-1] # step 为负 为逆序

# 二维
# x[Y,X] #Y轴索引,X轴索引
# x[Y1:Y2,X1:X2] # 索引切片范围查询
# x[Y1:Y2:step,X1:X2:step] # 索引切片范围查询,加索引步长

# print(x2)
# print(x2[2,1]) # Y轴索引为2,X轴索引为1的数据:6,,索引0 开始,y 轴(竖) 索引为2 ,x轴(横)为1的数据。
# print(x2[:2, :3]) # 切片范围查询,Y轴索引0到索引1 的数据。X轴索引0 到索引2的数据。

# print(x2[:3, ::2]) # Y轴索引0 到2,X轴索引全部但是索引的步长为2.

# print(x2[::-1, ::-1]) # Y轴和X轴,均逆序输出

# 三维切片
# x[Y:Z:X] ,Z轴是每块(每组)的索引

# arr1 = np.arange(24)
#
# # 重塑矩阵
# arr2 = arr1.reshape(4,2,3) # 4块、每块两行、每块3列。 #将arr1变为4×2×3=24 个元素的多维数组,row * col * 列中列,
# # print(arr2)
# print(arr2[2,1,0]) # 单个索引
# print(arr2[:3, :1,:3]) # 展示2块,每块1行。 因为arr2 最大每块就是2行。

5、np.newaxis 增加维度

# a=np.array([1,2,3,4,5])
# aa=a[np.newaxis,:]
# print(aa)
# print(aa.shape)

6、axis详解

# arr3 = np.arange(24).reshape(4,2,3)
#
# print(arr3)
# # print(arr3.min(axis=1))
# print(arr3.sum(axis=0))

# 三维
# axis=0 :Y轴方向(竖着)以组为单位。
# axis=1 :Z轴方向竖着比较,以组中的上下行 。
# axis=2 :x轴方向横着比较。

# 二维
# axis=0 :Y轴方向(竖着)
# axis=1 :x轴方向(横这)

7、数组的串联和分隔

# x = np.array([1, 2, 3])
# y = np.array([3, 2, 1])
# z = np.array([5,6,7])
# xyz = np.concatenate([x, y,z])
# print(xyz)

#
# boy = np.array([[1, 2, 3],
# [4, 5, 6]])
# girl = np.array([[8, 9,10],
# [43, 52, 61]])
#
# boy_girl = np.concatenate([boy,girl],axis=1)
# print(boy_girl)

#
# x = np.array([1, 2, 3])
# grid = np.array([[9, 8, 7],
# [6, 5, 4]])

# vertically(垂直) stack the arrays
# vs = np.vstack([x, grid])
# print(vs)

# 水平堆叠
# y = np.array([[99],
# [99]])
# hs = np.hstack([grid, y])
# print(hs)

# 分列数组
# x = [1, 2, 3, 99, 99, 3, 2, 1]
# x1, x2, x3 = np.split(x, [3, 5]) # 从索引3 到索引4 砍两刀
# print(x1, x2, x3)

# grid = np.arange(16).reshape((4, 4))
# print(grid)
# #
# print('------------------------')
#
# upper,lower = np.vsplit(grid, [3]) # 水平分隔
# print(upper)
# print('------------------------')
# print(lower)

# left, right = np.hsplit(grid, [1]) # 垂直分隔
# print(left)
# print('------------------------')
# print(right)

8、数组通用的函数

# x = np.arange(4)
# print("x =", x)
# print("x + 5 =", x + 5)
# print("x - 5 =", x - 5)
# print("x * 2 =", x * 2)
# print("x / 2 =", x / 2)
# print("x // 2 =", x // 2) # floor division
# print("x ** 2 = ", x ** 2)
# print("x % 2 = ", x % 2)

'''
Operator Equivalent ufunc Description
+ np.add Addition (e.g., 1 + 1 = 2)
- np.subtract Subtraction (e.g., 3 - 2 = 1)
- np.negative Unary negation (e.g., -2)
* np.multiply Multiplication (e.g., 2 * 3 = 6)
/ np.divide Division (e.g., 3 / 2 = 1.5)
// np.floor_divide Floor division (e.g., 3 // 2 = 1)
** np.power Exponentiation (e.g., 2 ** 3 = 8)
% np.mod Modulus/remainder (e.g., 9 % 4 = 1)

'''
# https://numpy.org/doc/stable/reference/ufuncs.html

################9、聚合函数####################

'''
功能名称 NaN安全版本 描述
np.sum np.nansum 计算元素总和
np.prod np.nanprod 计算元素的乘积
np.mean np.nanmean 计算元素均值
np.std np.nanstd 计算标准偏差
np.var np.nanvar 计算方差
np.min np.nanmin 求最小值
np.max np.nanmax 寻找最大值
np.argmin np.nanargmin 查找最小值的索引
np.argmax np.nanargmax 查找最大值的索引
np.median np.nanmedian 计算元素的中位数
np.percentile np.nanpercentile 计算元素的基于排名的统计信息
np.any 不适用 评估任何元素是否为真
np.all 不适用 评估所有元素是否为真
我们将在本书的其余部分中经常看到这些汇总。

'''

# 小案例
# import pandas as pd
# data = pd.read_csv('data/president_heights.csv')
# heights = np.array(data['height(cm)'])
# print("Mean height: ", heights.mean())
# print("Standard deviation:", heights.std())
# print("Minimum height: ", heights.min())
# print("Maximum height: ", heights.max())
#
# # 百分之25 的平均身高
# print("25th percentile: ", np.percentile(heights, 25))
# # 中位数的身高
# print("Median: ", np.median(heights))
# # 百分之75人的平均很高
# print("75th percentile: ", np.percentile(heights, 75))

9、广播的规则

'''
广播的规则
NumPy中的广播遵循一组严格的规则来确定两个数组之间的交互:

规则1:如果两个数组的维数不同,则维数较少的数组的形状将在其前(左侧)填充。
规则2:如果两个数组的形状在任何维度上都不匹配,则将在该维度上形状等于1的数组拉伸以匹配其他形状。
规则3:如果在任何维度上尺寸不一致,且两个尺寸都不等于1,则会引发错误。

'''

# 具有相同shape的数组
# a = np.array([0, 1, 2])
# b = np.array([5, 5, 5])
# print(a+b)

#数组加零维数组
# print(a+5) # 扩展或复制5到数组[5, 5, 5]

# 二维数组 + 一维数组
# M = np.ones((3, 3))
# print(M)
# print(M +a)

# 一维数组a被拉伸或在第二维上广播以匹配的形状。

# 两个广播,同时拉伸a 和b,匹配一个通用的形状。
# a = np.arange(3)
# b = np.arange(3)[:, np.newaxis]
#
# print(a)
# print(b)
# print(a+b)

'''

广播示例1

M = np.ones((2, 3))
a = np.arange(3)

让我们考虑对这两个数组的操作。数组的形状是

M.shape = (2, 3)
a.shape = (3,)
通过规则1可以看到数组a的维数较少,因此我们在数组的左侧填充了1维:

M.shape -> (2, 3)
a.shape -> (1, 3)

根据规则2,我们现在看到第一个维度不一致,因此我们拉伸该维度以使其匹配:

M.shape -> (2, 3)
a.shape -> (2, 3)
形状匹配,我们看到最终形状将是(2, 3):

M + a
array([[1.,2.,3.],
[1.,2.,3.]])

广播示例2

a = np.arange(3).reshape((3, 1))
b = np.arange(3)

再次,我们将从写出数组的形状开始:

a.shape = (3, 1)
b.shape = (3,)
规则1规定我们必须用1填充形状b:

a.shape -> (3, 1)
b.shape -> (1, 3)
规则2告诉我们,我们将每个升级,以匹配另一个数组的相应大小:

a.shape -> (3, 3)
b.shape -> (3, 3)
因为结果匹配,所以这些形状是兼容的。我们可以在这里看到:

广播示例3

M = np.ones((3, 2))
a = np.arange(3)

数组的形状是

M.shape = (3, 2)
a.shape = (3,)
再次,规则1告诉我们,必须使用填充形状a:

M.shape -> (3, 2)
a.shape -> (1, 3)
根据规则2,的第一个维度a被拉伸以匹配的第一个维度M:

M.shape -> (3, 2)
a.shape -> (3, 3)

最终形状不匹配,因此这两个数组不兼容导致报错ValueError
为了消除错误可以,增加数组维度,来进行配
a[:, np.newaxis].shape
(3, 1)
M + a[:, np.newaxis]

'''

10、数组条件比较

'''
== np.equal != np.not_equal
< np.less <= np.less_equal
> np.greater >= np.greater_equal

'''
# rng = np.random.RandomState(0)
# x = rng.randint(10, size=(3, 4))
# print(x)

# print(x<3)
# print(x[x<3]) # 布尔索引

# 计数,计算数组中小于3的数据有多少个
# print(np.count_nonzero(x<3))

# 同样用sum 也可以求出小于5的个数
# print(np.sum(x < 5))

# #每行有多少个小于6的值?
# print(np.sum(x<6,axis=1))

# print(np.all(x < 8, axis=1)) # 判断每行中的所有数据

# 判断每行中的任一一个数据小于8,就返回true.
# print(np.any(x < 8, axis=1))

11、numpy 核心布尔运算符

# 与:(x > 3) & (x < 6)
# print(np.sum((x > 3) & (x < 6)))

# print(np.sum((x <= 3))) # x<=3 是5个

# print(np.sum((x >=7))) # x>=7 是3 个

# 或:(x <= 3) | (x>= 7) )),匹配的条件更多
# 非:~
# print(np.sum(( ~(x <= 3) | (x>= 7) )))

12、fancy index 神奇索引(花样索引)

# rand = np.random.RandomState(42)
# x = rand.randint(100, size=10)
# print(x)

# 第一种方式,索引取值
# print([x[3], x[7], x[2]])

# 第二种方式
# ind = [3,7,4]
# print(x[ind])

# 第三种方式,这种为花样索引
# ind = np.array([[3, 7],
# [4, 5]])
# print(x[ind])

# X = np.arange(12).reshape((3, 4))
# print(X)
# row = np.array([0, 1, 2]) # Y轴
# col = np.array([2, 1, 3]) # X轴
#
# print(X[row, col])
#
# print('----------------------------')
# print(X [ row [:, np.newaxis ], col])

'''
在这里,每个行值都与每个列向量匹配,就像我们在广播算术运算中看到的那样。例如:
务必记住,使用花式索引时,返回值反映的是索引的广播形状,而不是所索引数组的形状。
'''

13、联合索引

print(X [ 2 , [ 2,0,1 ]]) # Y轴第二个,X轴索引为2,0,1 这三个数据

14、花式索引修改其值

# x = np.arange(10)
# i = np.array([2, 1, 8, 4])

# 把i 对应的索引的值都修改成99
# x[i] = 99
# print(x)

# 把i 对应的值都相应的减去10
# x[i] -= 10
# print(x)

# i = [2, 3, 3, 4, 4, 4]
# x[i] += 1
# print(x)

15、排序

# a = np.array([[1,4],[3,1]])
# print(a)
# aa= np.sort(a,axis=1) # 沿最后一个轴排序
# print(aa)

# bb= np.sort(a, axis=None) # 多维变一维进行排序
# print(bb)

# cc = np.sort(a, axis=0) # Y轴排序
# print(cc)

# 通过字段进行排序

# 使用order关键字指定对结构化数组进行排序时要使用的字段:
# dtype = [('name', 'S10'), ('height', float), ('age', int)]

# values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38),
# ('Galahad', 1.7, 38)]
# a = np.array(values, dtype=dtype) # create a structured array
# ab =np.sort(a, order='height')

# print(ab)

# 按年龄排序,如果年龄相等,则按身高排序:
# abc = np.sort(a, order=['age', 'height'])
# print(abc)

16、创建结构化数组

# 第一种方式
# nd1 = np.dtype({'names':('name', 'age', 'weight'),
# 'formats':('U10', 'i4', 'f8')})
#
# # 第二种方式
# nd2 = np.dtype({'names':('name', 'age', 'weight'),
# 'formats':((np.str_, 10), int, np.float32)})
# # 第三种方式
# nd3 = np.dtype('S10,i4,f8')
# print(nd3)

'''
Character Description Example
'b' Byte np.dtype('b')
'i' Signed integer np.dtype('i4') == np.int32
'u' Unsigned integer np.dtype('u1') == np.uint8
'f' Floating point np.dtype('f8') == np.int64
'c' Complex floating point np.dtype('c16') == np.complex128
'S', 'a' String np.dtype('S5')
'U' Unicode string np.dtype('U') == np.str_
'V' Raw data (void) np.dtype('V') == np.void

'''

# np.array([1, 2, 3]) # 创建一维数组
# np.array([[1, 2], [3, 4]]) # 创建二维数组
# np.array([1, 2, 3], ndmin=2) # 通过ndmin 来指定创建2维数组
#
# str_arr = np.array([1, 2, 3], dtype=str) #提供类型来创建数组
# print(str_arr)
#
# # a,b 为字段名称,'<i4' 为int 数据类型
# x = np.array([(1,2),(3,4)],dtype=[('a','<i4'),('b','<i4')])
# print(x['b'])

# 创建一个矩阵,就是一个二维ndarray简化版
# mat_arr = np.array(np.mat('1 2 ; 3 4'))
# print(mat_arr)

参考来源:利用Python进行数据分析

1、生成随机数组


import numpy as np

'''
用来存储和处理大型矩阵,
比Python自身的嵌套列表(nested list structure)结构要高效的多
,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库
'''
data = np.random.rand(2,3)
print(data)
print(data*10) # 所有元素都乘以了10
print(data+data) # 数组中的对应元素进行了相加

2、生成ndarray

data1 = [6,7.5,8,0,1]
arr1 = np.array(data1)
print(arr1)

data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
print(arr2) # 因为是嵌套列表,so,形成了二维数组

# 通过检查ndim,shape属性来确认
arr2.ndim #阵列的轴数(维度)
arr2.shape

#判断数组元素类型
arr1.dtype

# 创建数组
np.zeros(10)
np.zeros((3,6))
np.empty((2,3,2))
np.arange(15)

3.ndarray 数据类型

arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
arr1.dtype
arr2.dtype
arr = np.array([1, 2, 3, 4, 5])
arr.dtype
float_arr = arr.astype(np.float64)
float_arr.dtype
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
arr
arr.astype(np.int32)
numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
numeric_strings.astype(float)
int_array = np.arange(10)
calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
int_array.astype(calibers.dtype)
empty_uint32 = np.empty(8, dtype='u4')
empty_uint32

4、Numpy数组运算

arr = np.array([[1., 2., 3.], [4., 5., 6.]])
arr
arr * arr
arr - arr
1 / arr
arr ** 0.5
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
arr2
arr2 > arr

5、基础索引与切片

arr
arr[5]
arr[5:8]
arr[5:8] = 12
arr

arr_slice = arr[5:8]
arr_slice

arr_slice[1] = 12345
arr
arr_slice[:] = 64
arr

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[2]

arr2d[0][2]
arr2d[0, 2]

arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d
arr3d[0]
old_values = arr3d[0].copy()
arr3d[0] = 42
arr3d
arr3d[0] = old_values
arr3d
arr3d[1, 0]
x = arr3d[1]
x
x[0]

6、切片索引

arr
arr [1:6 ]

arr2d
arr2d [:2 ]

arr2d [:2 , 1 :]

arr2d [ 1 , :2 ]

arr2d [:2, 2 ]

arr2d [:, :1 ]

arr2d [:2,1:] = 0

7、 布尔索引

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data = np.random.randn(7, 4) #生成一些正态分布的数据 7行 4列
names
data
names == 'Bob'
data[names == 'Bob'] # 把布尔值数组作为data 的索引。 注意:布尔值数组的长度必须和数组轴索引长度一致,
data[names == 'Bob', 2:]
data[names == 'Bob', 3]

names != 'Bob'
data[~(names == 'Bob')]

cond = names == 'Bob'
data[~cond]
mask = (names == 'Bob') | (names == 'Will')
mask
data[mask]
data[data < 0] = 0
data
data[names != 'Joe'] = 7
data

8、神奇索引


'''
神奇索引是Numpy中的术语,用于描述使用整数数组进行数据索引

'''
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
arr

arr[[4, 3, 0, 6]]
arr[[-3, -5, -7]]

arr = np.arange(32).reshape((8, 4))
arr
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]

9、数组转置和换轴

'''
转置是一种特殊的数据重组形式,可以返回底层数据的视图而不需要复制任何内容
数组拥有transpose方法,也有特殊的T属性

'''
arr = np.arange(15).reshape((3, 5))
arr
arr.T
arr = np.random.randn(6, 3)
arr

#如果arr5和arr6都是二维数组,那么它返回的是矩阵乘法。
arr5 = np.array([[2,3],[4,5]])
arr6 = np.array([[6,7],[8,9]])
np.dot(arr5,arr6)

# 1、 2 * 6 + 3 * 8 = 36
# 2、 2 * 7 + 3 * 9 = 41

# 3、 4 * 6 + 5 * 8 = 64
# 4、 4 * 7 + 5 * 9 = 73

arr = np.arange(16).reshape((2, 2, 4))
arr
arr.transpose((1, 0, 2))
arr
arr.swapaxes(1, 2) # swapaxes 返回的是数据的视图,而没有对数据进行复制

10、通用函数

arr
np.sqrt(arr)
np.exp(arr)
x = np.random.randn(8)
y = np.random.randn(8)
x
y
np.maximum(x, y)
arr = np.random.randn(7) * 5
arr
remainder, whole_part = np.modf(arr)
remainder
whole_part
arr
np.sqrt(arr)
np.sqrt(arr, arr)
arr

11、使用数组进行面向数组编程

points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
xs, ys = np.meshgrid(points, points)
ys
z = np.sqrt(xs ** 2 + ys ** 2)
z
import matplotlib.pyplot as plt
plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
plt.draw()
plt.close('all')

12、将条件逻辑作为数组操作

xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = [(x if c else y)
for x, y, c in zip(xarr, yarr, cond)]
result
result = np.where(cond, xarr, yarr)
result
arr = np.random.randn(4, 4)
arr
arr > 0
np.where(arr > 0, 2, -2)
np.where(arr > 0, 2, arr) # set only positive values to 2

13、数学和统计方法

arr = np.random.randn(5, 4)
arr
arr.mean()
np.mean(arr)
arr.sum()
arr.mean(axis=1)
arr.sum(axis=0)
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
arr.cumsum()
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
arr
arr.cumsum(axis=0)
arr.cumprod(axis=1)

14、布尔值数组的方法

arr = np.random.randn(100)
(arr > 0).sum() # Number of positive values
bools = np.array([False, False, True, False])
bools.any()
bools.all()

15.排序

arr
arr.sort()
arr
arr = np.random.randn(5, 3)
arr
arr.sort(1)
arr
large_arr = np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05 * len(large_arr))] # 5% quantile

16 、唯一值与其它集合逻辑

names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(ints)
sorted(set(names))
values = np.array([6, 0, 0, 3, 2, 5, 6])
np.in1d(values, [2, 3, 6])

17、使用数组进行文件输入和输出

arr = np.arange(10)
np.save('some_array', arr)
np.load('some_array.npy')
np.savez('array_archive.npz', a=arr, b=arr)
arch = np.load('array_archive.npz')
arch['b']
np.savez_compressed('arrays_compressed.npz', a=arr, b=arr)

18、线性代数

y = np.array([[6., 23.], [-1, 7], [8, 9]])
x
y
x.dot(y)
np.dot(x, y)
np.dot(x, np.ones(3))
x @ np.ones(3)
from numpy.linalg import inv, qr
X = np.random.randn(5, 5)
mat = X.T.dot(X)
inv(mat)
mat.dot(inv(mat))
q, r = qr(mat)
r

Numpy快速入门

基础知识

# 基础知识
# NumPy的主要对象是齐次多维数组。它是一个元素表(通常是数字),所有相同的类型,由正整数的元组索引。在NumPy维度被称为轴。轴的数量是等级。
#
# 例如,三维空间中一个点的坐标[1,2,1]是一个等级为1的数组,因为它具有一个坐标轴。该轴的长度为3.
#
# 在下面的示例中,该数组具有等级2(它是二维的)。第一维(轴)的长度为2,第二维的长度为3。
#
# [[ 1. , 0. , 0 ],
# [ 0. , 1. , 2. ]]
# NumPy的数组类叫做ndarray。它也被别名数组所知 。请注意,numpy.array与标准Python库类array.array不一样,它只处理一维数组,并且提供较少的功能。ndarray对象的更重要的属性是:
#

# ndarray.ndim
# 阵列的轴数(维度)。在Python世界中,维度的数量被称为等级。

# ndarray.shape
# 数组的尺寸。这是一个整数的元组,指示每个维度中数组的大小。对于有n行m列的矩阵,形状将是(n,m)。形状元组的长度 因此是等级或维数 ndim。

# ndarray.size
# 数组元素的总数。这等于形状的元素的乘积。
# ndarray.dtype
# 一个描述数组中元素类型的对象。可以使用标准的Python类型创建或指定dtype。另外NumPy提供它自己的类型。numpy.int32,numpy.int16和numpy.float64是一些例子。
# ndarray.itemsize
# 数组中每个元素的字节大小。例如,类型为float64的元素的数组具有项目大小 8(= 64/8),而类型complex32中的一个具有项目大小 4(= 32/8)。这相当于ndarray.dtype.itemsize。
# ndarray.data
# 包含数组的实际元素的缓冲区。通常,我们不需要使用这个属性,因为我们将使用索引设施访问数组中的元素。
'''

import numpy as np #可用来存储和处理大型矩阵的工具
from numpy import pi
import matplotlib.pyplot as plt

a = np.arange(15).reshape(3, 5) # 默认是从生成0-14个数字,分成三组,每组5个元素。

np.random.randint(11,size=(2,3)) #通过随机生成二维三列的矩阵

print(a)

a.shape #数组的尺寸
a.ndim #阵列的轴数
a.dtype.name #一个描述数组中元素类型的对象
a.itemsize #数组中每个元素的字节大小
a.size #数组元素的总数
type(a)

b = np.array([6, 7, 8.1,'ztloo',1]) #创建一个数组
b
b.dtype
type(b) #获取变量b的数据类型

np.array([(1.5, 2, 3), (4, 5, 6)]) #数组将序列序列转换成二维数组,将序列序列转换成三维数组,等等
np.array([[1, 2], [3, 4]], dtype=complex) #数组的类型也可以在创建时明确指定:,complex:复杂类型

#NumPy提供了几个函数来创建具有初始占位符内容的数组。 这样可以最大限度地减少增加阵列的成本

np.zeros((3,4)) #创建全是零的二维,三组4列的元素。默认情况下,创建的数组的dtype是float64。
np.ones((2,3,4), dtype=np.int16) #创建全是1的三维数组,分两组,4列。
np.empty((2,3))#空函数创建一个数组,其初始内容是随机的,取决于内存的状态
np.arange(10, 30, 5) #返回一个范围数组数组
np.linspace( 0, 2, 9 ) #arange与浮点参数一起使用时,由于有限的浮点精度,获得不到元素的数量。 使用函数linspace作为参数来接收我们想要的元素的数量

x = np.linspace( 0, 2*pi, 100 ) #用于评估许多点的功能
f = np.sin(x)

np.arange(6) #一维数组
np.arange(12).reshape(4,3) #二维数组
np.arange(24).reshape(2,3,4) #三维数组

np.arange(10000) #如果数组太大而无法打印,NumPy会自动跳过数组的中心部分,只打印角点:

创建矩阵

np.mat([[1,2,3],[4,5,6]])

np.mat(np.arange(10)) #把数组转换成矩阵

#数组上的算术运算符应用于元素。 一个新的数组被创建并填充结果
a = np.array([20, 30, 40, 50])
b = np.arange(4)
b
a-b
b**2
10*np.sin(a)
a<35

#与许多矩阵语言不同,运算符 *在NumPy数组中以元素形式操作。 矩阵乘积可以使用点函数或方法执行:
A = np.array( [[1,1],[0,1]] )
B = np.array( [[2,0],[3,4]] )
A*B

A.dot(B) #所得到的数组中的每个元素为,第一个矩阵中与该元素行号相同的元素与第二个矩阵与该元素列号相同的元素,两两相乘后再求和。

#A第一行元素:[1,1] * B第一列元素 [2,3] = 1*2 +1*3 = 5 =X = array([[X, Y], [X1, Y1]])
#A第一行元素:[1,1] * B第二列元素 [0,4] = 1*0 +1*4 = 4 =Y = array([[X, Y], [X1, Y1]])

#A第二行元素:[0,1] * B第一列元素 [2,3] = 0*2 +1*3 = 3 =x1 = array([[X, Y], [X1, Y1]])
#A第二行元素:[0,1] * B第二列元素 [0,4] = 0*0 +1*4 = 4 =y1 = array([[X, Y], [X1, Y1]])

# 最终输出结果:array([[5, 4], [3, 4]])

#例如+=和*=)适当地修改现有数组,而不是创建一个新数组。
a = np.ones((2,3), dtype=int)
b = np.random.random((2, 3))
a *= 3
a
b += a
b
a += b #报错,因为b不会自动转换为整数类型

#在使用不同类型的数组时,结果数组的类型对应于更更精确的数组(称为上传)。

a = np.ones(3, dtype=np.int32)
b = np.linspace(0, pi, 3)
b.dtype.name
c = a+b
c
c.dtype.name
d = np.exp(c*1j) #计算各元素的指数值
d
d.dtype.name

一元运算

a = np.random.random((2,3))
a
a.sum()
a.min()
a.max()
a.mean()

a = np.random.random((2,3))
a
a.sum()
a.min()
a.max()
a.mean()

#通过指定轴参数,您可以沿着数组的指定轴应用操作

b = np.arange(12).reshape(3, 4)
b
b.sum(axis=0)#每列的总和
b.min(axis=1)#每行最小的数
b.cumsum(axis=1)#每行的累计和,每行除开头外,第二个数是开头数+本身,进行累计。第三个数=第二个数+第三个数

#常用的数学函数,如sin,cos和exp。 在NumPy中,这些被称为“通用函数”(ufunc)

B = np.arange(3)
B
np.exp(B)
np.sqrt(B)
C = np.array([2., -1., 4.])
np.add(B, C)

列表一样的一维数组,既能索引又能

a = np.arange(10)**3
a
a[2]
a[2:5]
a[:6:2] = -1000 #相当于[0:6:2] = -1000; 从开始到第6位,独占,每第二个元素设置为-1000
a

a[::-1] #把a反向输出

for i in a:
print(i ** (1 / 3.))

#多维数组每个轴可以有一个索引。 这些索引用逗号分隔: [y的索引:x的索引] =[y+1:x+1]

def f(x,y):
return 10 * x + y

b = np.fromfunction(f,(5,4),dtype=int) #以f函数式创建数组 ,创建整数数组,y轴为5,x轴为4的数组,
b
b[2, 3]#索引是从零开始,y轴=3,x轴为2 的数为=23

b[0:5, 1] #第二列中的每一行,0:5 是从y轴的取值范围,索引:1 相当于x轴的第二列、

b[ : ,1] #相当于b[0:5, 1],不给取值范围,默认去x轴第二列

b[1:3, :] #第二行和第三行中的每一列。

#当提供的索引数量少于轴数时,缺失的索引被认为是完整的切片:

b[-1] #最后一行 相当于b [-1 ,:]

# a 3D array (two stacked 2D arrays)
c = np.array( [[[ 0, 1, 2], [ 10, 12, 13]],[[100,101,102],[110,112,113]]])
c.shape
c[1,...] #同c [1,:,]或c [1]
c[...,2] #与c [:,:,2]相同

迭代多维数组是相对于第一个轴完成的:

for row in b:
    print(row)

对数组中的每个元素执行操作

for element in b.flat:
    print(element)

改变数组的形状

a = np.floor(10*np.random.random((3,4)))
a
a.shape

a.ravel()#返回展开的数组变成一维度。

a.reshape(6,2) #返回具有修改形状的数组,y变成6行,x变成2列。

a.T #返回数组,转置 x和y轴互换。
a.T.shape
a.shape

a.resize((2,6)) #y轴变两行。x轴变6列。
a

a.reshape(3,-1)#y轴变3行。x轴其-1,会自动计算x轴的列数。

#几个阵列可以沿不同的轴堆叠在一起:

a = np.floor(10*np.random.random((2,2)))
a
b = np.floor(10*np.random.random((2,2)))
b

np.vstack((a,b)) #b数组拼接在a数组的y轴的负方向上
np.hstack((a,b)) #b数组拼接在a数组的x轴的正方向上

#函数column_stack将 一维数组作为列叠加到2维数组中。它相当于仅用于一维数组的vstack:
from numpy import newaxis
np.column_stack((a,b)) # 把a的x和b的x轴的第一行和第二行分别拼接。

a = np.array([4.,6.])
b = np.array([3.,8.])

a[:,newaxis] #这允许有一个2D列向量,

np.column_stack((a[:,newaxis],b[:,newaxis]))

np.vstack((a[:,newaxis],b[:,newaxis])) #把a,b的x变成y,然后进行串联拼接

np.r_[1:4, 0, 4]

#使用hsplit,可以通过指定要返回的相同形状的数组的数量,或通过指定分割应该发生之后的列来沿着其横轴拆分数组:

a = np.floor(10*np.random.random((2,12)))
a
np.hsplit(a, 3) # 沿着x轴拆分成3列(4,4,4)

np.hsplit(a,(3,4)) # 沿着x轴拆分成3列,然后再把第四列拆分为一组。由(4,4,4)变(3,1,8)

#当操作和操作数组时,它们的数据有时会被复制到一个新的数组中,这通常是初学者混淆的来源。有三种情况:

a = np.arange(12)
b = a # 不创建新对象
b is a
b.shape = 3, 4 ##改变形状
a.shape

#Python将可变对象作为引用传递,所以函数调用不会复制。
def f(x):
    print(id(x))
id(a) #id是对象的唯一标识符
f(a)

c = a.view()
c is a
c.base is a #c是由拥有的数据的视图
c.flags.owndata
c.shape = 2,6 # 一个的形状不改变
a.shape
c[0,4] = 1234 # a的数据变化
a

s = a[ : , 1:3]
# 为了清晰起见,添加了#个空格; 也可以写成“s = a [:,1:3]”
s[:] = 10 #s [:]是s的视图。注意S = 10和s之间的差[:] = 10
a

深复制;该复制方法使阵列及其数据的完整副本

d = a.copy() #创建新数据的新数组对象
d is a

d.base is a # d不与分享任何

d[0,0] = 9999
a

常用的函数大全

数组创建


#-arange, array, copy, empty, empty_like, eye, fromfile,

#-fromfunction, identity, linspace,
    logspace, mgrid, ogrid, ones, ones_like,
#- r, zeros, zeros_like

转换

#- ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

手法

#--- array_split, column_stack, concatenate, diagonal, dsplit,dstack, hsplit, hstack,
#--- ndarray.item, newaxis, ravel, repeat, reshape, resize,
#--- squeeze, swapaxes, take, transpose, vsplit, vstack

问题

#--- all, any, nonzero, where

函数指令

#- argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作

#--- choose, compress, cumprod, cumsum, inner, 
ndarray.fill, imag, prod, put, putmask, real, sum

基本统计

#--- cov, mean, std, var

基本的线性代数

#--- cross, dot, outer, linalg.svd, vdot

Numpy的API

---https://docs.scipy.org/doc/numpy-dev/reference/routines.html#routines

a = np.arange(12)**2 # 前12个平方数
i = np.array( [ 1,1,3,8,5 ] ) # 一个索引的阵列
a[i] # 的在位置上的元件我

j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # 索引的二维阵列
a[j] # 相同的形状为J

# -----当索引数组a是多维时,单个索引数组指向a的第一维。 以下示例通过使用调色板将标签图像转换为彩色图像来显示此行为。--------#

palette = np.array( [ [0,0,0], # 黑色
[255,0,0], # 红色
[0,255,0], # 绿色
[0,0,255], # 蓝色
[255,255,255] ] ) # 白色
image = np.array( [ [ 0, 1, 2, 0 ], # 每一值对应于在调色板中的颜色
[ 0, 3, 4, 0 ] ] )

palette[image] # (2,43)彩色图像

# 可以给一个以上的维度指标。每个维度的索引数组必须具有相同的形状。

a = np.arange(12).reshape(3,4)
a

i = np.array( [ [0,1], # 用于第一暗淡的指数
[1,2] ] )

j = np.array( [ [2,1], # 第二个暗淡的指数
[3,3] ] )

a[i,j] #i和j必须具有相同的形状,a(0,2) =2 a(1,1)=5 ,a(1,3) =7 ,a(2,3) =11 最终结果-->array([[ 2, 5],[ 7, 11]])

a[i,2]#i和j必须具有相同的形状,a(0,2) =2 a(1,2)=6 , a(1,2)=6 ,a(2,2) =10 最终结果-->array([[ 2, 6], [ 6, 10]])

a[:,j] # i.e., a[ : , j]

# 可以把i和j放在一个序列中(比如说一个列表),然后用列表进行索引。
l = [i,j]
a[l] # 等同于[i,j]

s = np.array([i,j])
a[s] # 出错,不能通过将i和j放入一个数组。

a[tuple(s)] # 相同作为[i,j]

# 数组索引的另一个常见用途是搜索时间相关系列的最大值:
time = np.linspace(20, 145, 5) # 时标
data = np.sin(np.arange(20)).reshape(5,4) # 4随时间变化的一系列
time

data

ind = data.argmax(axis=0) # 每个系列最大值的索引
ind

time_max = time[ind] # 对应于最大值的次数

data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...

time_max

data_max

np.all(data_max == data.max(axis=0))

# 使用数组的索引作为分配给的目标:

a = np.arange(5)
a

a[[1,3,4]] = 2
a

# 当索引列表包含重复时,分配会执行好几次,而留下最后一个值

a = np.arange(5)
a #输出array([0, 1, 2, 3, 4])

#索引0 更为为1,第二次索引更改为2,所以数组的a处的零索引取值为原数组的为2索引的值:2 ,然后索引2处的数改为索引3的数,
a[[0,0,2]]=[1,2,3]

a

# 这足够合理,但要小心如果你想使用Python的 + =构造,因为它可能不会做你期望的:

a = np.arange(5)
a[[0,0,2]]+=1
a

# 布尔索引最自然的方法就是使用与原始数组形状相同的布尔数组:

a = np.arange(12).reshape(3,4)
b = a > 4
b # b为具有的形状的布尔

a[b] # 一维数组与选定的元素

# 这个属性在作业中非常有用:

a[b] = 0 #'A'大于4的所有元素用零代替
a

# 使用布尔索引来生成Mandelbrot集的图像:

def mandelbrot( h,w, maxit=20 ):
"""返回Mandelbrot分形的图像大小(h,w )."""
y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
c = x+y*1j
z = c
divtime = maxit + np.zeros(z.shape, dtype=int)

for i in range(maxit):
z = z**2 + c
diverge = z*np.conj(z) > 2**2 # 正在发偏离的人
div_now = diverge & (divtime==maxit) # 现在偏离的人
divtime[div_now] = i # 注意何时
z[diverge] = 2 # 避免偏离太多

return divtime
plt.imshow(mandelbrot(400,400))
plt.show()

# 用布尔值编制索引的第二种方法更类似于整数索引; 对于数组的每个维度,我们给出一个1D布尔数组,选择我们想要的切片:

a = np.arange(12).reshape(3,4)

a

b1 = np.array([False,True,True]) # 第一个dim选择

b1

b2 = np.array([True,False,True,False]) # 第二个dim选择

b2

a[b1,:] # 选择行(b1([False, True, True]),第一行不显示
a[b1] # 一样
a[:,b2] # 选择列
a[b1,b2] # 一个奇怪的事情要做

# ix_()函数:该ix_函数可用于不同的载体结合,以便获得对于每一个n-uplet结果。
# 例如,如果要计算从每个矢量a,b和c取得的所有三元组的所有a + b * c:

a = np.array([2,3,4,5])
b = np.array([8,5,4])
c = np.array([5,4,6,8,3])
ax,bx,cx = np.ix_(a,b,c)
ax
bx
cx
ax.shape, bx.shape, cx.shape
result = ax+bx*cx
result

# 你也可以按如下方式执行reduce:

def ufunc_reduce(ufct, *vectors):
vs = np.ix_(*vectors)
r = ufct.identity
for v in vs:
r = ufct(r,v)
return r

ufunc_reduce(np.add,a,b,c)

# 用字符串索引
# ----参考:https://docs.scipy.org/doc/numpy-dev/user/basics.rec.html#structured-arrays

# 线性代数

a = np.array([[1.0, 2.0], [3.0, 4.0]])
a
a.transpose() #x,y转换
np.linalg.inv(a)
u = np.eye(2) # 2×2矩阵; “眼睛”代表“我”
u
j = np.array([[0.0, -1.0], [1.0, 0.0]])
np.dot (j, j) # 矩阵产品
np.trace(u) # 返回数组的对角线的和。
y = np.array([[5.], [7.]])
np.linalg.solve(a, y)

np.linalg.eig(j)

# 参数:
# 方矩阵
# 返回
# 特征值,每个特征根据其多重性重复。
# 归一化的(单位“长度”)特征向量,
# 列``v [:,i]`是对应的特征向量
# 特征值``w [i]``。

# 技巧和提示: 要更改数组的尺寸,可以省略其中一个尺寸,然后自动推导出这些尺寸

a = np.arange(30)
a.shape = 2,-1,3 # -1表示“任何需要”
a.shape
a

# 矢量堆叠

x = np.arange(0,10,2) # x=([0,2,4,6,8])
y = np.arange(5) # y=([0,1,2,3,4])
m = np.vstack([x,y]) # m=([[0,2,4,6,8],
# [0,1,2,3,4]])
xy = np.hstack([x,y]) # xy =([0,2,4,6,8,0,1,2,3,4])

# 直方图

# #构建10000个正常偏离的矢量方差0.5 ^ 2和平均2
mu, sigma = 2, 0.5
v = np.random.normal(mu,sigma,10000)
# #绘制一个规格化的直方图与50箱
plt.hist(v, bins=50, normed=1) # matplotlib版本(图)
plt.show()

# #用numpy计算直方图,然后绘制
(n, bins) = np.histogram(v, bins=50, normed=True) # #NumPy版本(无图)
plt.plot(.5*(bins[1:]+bins[:-1]), n)
plt.show()

###################

# 使用pickle序列化numpy array

import pickle
x = np.arange(10)
x

f = open('x.pkl', 'wb')
pickle.dump(x, f) #b把x数组序列化到磁盘上为x.pkl文件
f = open('x.pkl', 'rb')
pickle.load(f) #在从文件中反序列化回来
np.save('one_array', x) #通过save方法来进行序列化到磁盘
np.load('one_array.npy') #通过load方法直接反序列化
y = np.arange(20)
np.savez('two_array.npz',a=x,b=y) #可以压缩多个数组到一个文件当当中
c = np.load('two_array.npz')
c['a']
c['b']

a =np.array([(1.5, 2, 3), (6, 4, 8)]) #数组将序列序列转换成二维数组,将序列序列转换成三维数组,等等
a
a.sort()
a
a.dtype
type(a)

1-OpenCV-图像处理入门

zhangtongle阅读(3191)

一、OpenCV简介

OpenCV于1999年由Gary Bradsky在英特尔创立,第一个版本于2000年问世。随后Vadim Pisarevsky加入了Gary Bradsky,主要负责管理英特尔的俄罗斯软件OpenCV团队

OpenCV现在支持与计算机视觉和机器学习相关的众多算法,并且正在日益扩展。

OpenCV支持各种编程语言,如C ++,Python,Java等,可以在不同的系统平台上使用,包括Windows、Linux,OS X,Android和iOS。基于CUDA和OpenCL的高速GPU操作接口也在积极开发中。
OpenCV-Python是OpenCV的Python API,结合了OpenCV C++ API和Python语言的最佳特性。

如果你使用Python作为编写OpenCV的程序,你将要熟练使用Python 和Numpy.

二、图像处理入门

2.1 读取图像

使用cv.imread()函数用于读取图像,其中函数的第一个参数为要读取的图像名称,此图像应该处在Python代码文件的工作目录中,或者应给出完整的文件路径。第二个参数是一个标志,以下几种参数分别指定了应该读取图像的方式。

  • cv.IMREAD_COLOR:以彩色模式加载图像,任何图像的透明度都将被忽略。这是默认参数。
  • cv.IMREAD_GRAYSCALE:以灰度模式加载图像。
  • cv.IMREAD_UNCHANGED:包括alpha通道的加载图像模式。

注意:或者你可以简单的传递1、0或者-1来替代上面三个标志。
参考以下代码:

import numpy as np
import cv2 as cv
# Load an color image in grayscale
img = cv.imread('messi5.jpg',0)

注意:如果加载图像的路径有错误,它并不会报错,而是返回给你一个None值。

2.2 显示图像

使用cv.imshow()函数用于在窗口中显示图像,窗口会自动适合图像大小。
其中函数的第一个参数是窗口的名称,以字符串类型表示。第二个参数是要加载的图像。
你可以显示多个图像窗口,只要它们的窗口名称不同就可以。

参考以下代码:

cv.imshow('image',img)
cv.waitKey(0)
cv.destroyAllWindows()

cv.waitKey()是一个键盘事件函数,它的参数是以毫秒为单位的时间。该函数等待参数时间,如果时间之内有键盘事件触发则程序继续,如果函数参数设置为0,则无限时间的等待键盘事件触发。它也可以设置为检测指定按键的触发,比如等待按键a的触发,我们将在下面讨论。

注意:这个函数除了可以等待键盘事件的触发之外还可以处理很多其他的GUI事件,所以你必须把它放在显示图像函数之后。

cv.destroyAllWindow()函数用于关闭我们所创建的所有显示图像的窗口,如果想要关闭任何特定的窗口,请使用cv.destroyWindow()函数,其中把要关闭的窗口名称作为参数传递。

注意:一种特殊的情况是,你也可以先创建一个窗口,之后再加载图像。这种情况下你可以自行决定窗口的大小,你可以使用cv.nameWindow()函数进行窗口大小的调整。默认函数参数是cv.WINDOW_AUTOSIZE,你可以将其改成cv.WINDOW_NORMAL,这样你就可以自行调整窗口大小了。当图像尺寸太大或者需要添加轨迹条时,调整窗口大小将会非常有用。

参考以下代码:

cv.namedWindow('image', cv.WINDOW_NORMAL)
cv.imshow('image',img)
cv.waitKey(0)
cv.destroyAllWindows()

2.3 保存图像

使用cv.imwrite()函数用于保存图像。其中第一个函数是文件名,第二个函数是你要保存的图像。
参考一下代码:

cv.imwrite('messigray.png',img)

总结
下面的代码程序将加载灰度图像,显示图像,如果按's'并退出则保存图像,或者按ESC键直接退出而不保存。

参考一下代码:
import numpy as np、
import cv2 as cv
img = cv.imread('messi5.jpg',0)
cv.imshow('image',img)
k = cv.waitKey(0)
if k == 27:  # wait for ESC key to exit
    cv.destroyAllWindows()
elif k == ord('s'): # wait for 's' key to save and exit
    cv.imwrite('messigray.png',img)
cv.destroyAllWindows()

# 注意:如果你使用的是64位计算机,则必须将k = cv.waitKey(0)修改为:
# k = cv.waitKey(0) & 0xFF

Python问题解决系列

zhangtongle阅读(2804)

前言

当你学完Python,可以使用老师模板化的代码,完成一些特定领域的功能,完全没有问题,当你接触新需求的时候,感觉自己的Python 白学了,写写代码,哇,报错 了,抓头,写写代码,哇出来的效果不是我想要的,小朋友你是否有很多问号?

总结Python容易产生问号的大全参考于Python社区。

问题1:不要直接在函数中使用外部变量

x = 10
def foo():
    x += 1

#报错信息:
UnboundLocalError: local variable 'x' referenced before assignment

#报错原因:
#在Python中,仅在函数内引用的变量是隐式全局变量。
#如果在函数体内的任何位置为变量赋值,则除非明确声明为全局,否则将其视为局部值。

#解决:
x = 10
def foo():
    global x
    x += 1

#使用面向对象编程
a =2
class F:
    def foo(self):
        global a
        a = a + 2
        print(a)
if __name__ == '__main__':
    F().foo()

问题2:跨模块共享全局变量

config.py:
x = 0 #  “x”配置设置的默认值

mod.py:
import config
config.x = 1

main.py:
import config
import mod
print(config.x)
# 请注意,出于同样的原因,使用模块也是实现Singleton设计模式的基础

问题3:将可选或关键字参数传递给另一个函数

def g(x, *args, **kwargs):
    print(x,kwargs)

def f(x, *args, **kwargs):
    kwargs['width'] = '14.3c'
    g(x, *args, **kwargs)

if __name__ == '__main__':
    f(1)

问题4:为什么更改列表 'y' 也会更改列表 'x'?

x = []
y = x
y.append(10)
y # [10]
x # [10]

产生这种结果有两个因素:

  1. 变量只是指向具体对象的名称。 执行 y = x 并不会为列表创建一个副本 —— 它只是创建了一个新变量 y 指向 x 所指向的同一对象。 这意味着只存在一个对象(列表),x 和 y 都是对它的引用。

  2. 列表属于 mutable 对象,这意味着你可以改变它的内容。

在调用 append() 之后,这个可变对象的内容由 [] 变为 [10]。 由于两个变量都指向同一对象,因此使用任何一个名称所访问到的都是修改后的值 [10]。

如果我们改为将不可变对象赋值给 x:

>>>
>>> x = 5  # ints are immutable
>>> y = x
>>> x = x + 1  # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5

我们可以看到在此情况下 x 和 y 就不再相等了。

这是因为整数是 immutable 对象,当我们执行 x = x + 1 时我们并不是改变了 5 这个对象的值;而是创建了一个新的对象 (整数 6) 并将其赋值给 x (也就是改变了 x 所指向的对象)。

在赋值之后我们就有了两个对象 (整数 6 和 5) 以及分别指向它们的两个变量 (x 现在指向 6 而 y 仍然指向 5)。

某些操作 (例如 y.append(10) 和 y.sort()) 是改变原对象,而看上去相似的另一些操作 (例如 y = y + [10] 和 sorted(y)) 则是创建新对象。

通常在 Python 中 (以及在标准库的所有代码中) 会改变原对象的方法将返回 None 以帮助避免混淆这两种不同类型的操作。 因此如果你错误地使用了 y.sort() 并期望它将返回一个经过排序的 y 的副本,你得到的结果将会是 None,这将导致你的程序产生一个容易诊断的错误。

但是,还存在一类操作,不同的类型执行相同的操作会有不同的行为:那就是增强赋值运算符。
例如,+= 会原地改变列表,但不会改变元组或整数 (a_list += [1, 2, 3] 与 a_list.extend([1, 2, 3]) 一样都会改变 a_list,而 some_tuple += (1, 2, 3) 和 some_int += 1 则会创建新的对象)。
换而言之:

  • 如果我们有一个可变对象 (list, dict, set 等等),我们可以使用某些特定的操作来改变它,所有指向它的变量都会显示它的改变。

  • 如果我们有一个不可变对象 (str, int, tuple 等等),所有指向它的变量都将显示相同样的值,但凡是会改变这个值的操作将总是返回一个新对象。

如果你想知道两个变量是否指向相同的对象,你可以使用 is 运算符,或内置函数 id()。

问题5:复制对象?

一般来说,通常情况下请尝试 copy.copy() 或 copy.deepcopy()。 不是所有对象都可以复制,但多数都是可以的。
某些对象可以方便地复制。 例如字典具有 copy() 方法:


newdict = olddict.copy()
序列可以通过切片来复制:
new_l = l[:]

问题6:-22 // 10返回-3?

这主要是为了让 i % j 的正负与 j 一致,如果你想要这样的结果,并且又想要:
i == (i // j) * j + (i % j)
那么整除就必须向下取整。 C 同样要求保持一致,并且编译器在截短 i // j 的结果值时需要使 i % j 的正负与 i 一致。
对于 i % j 来说 j 为负值的应用场景实际上是非常少的。 而 j 为正值的情况则非常多,并且实际上在所有情况下让 i % j 的结果为 >= 0 会更有用处。
如果现在时间为 10 时,那么 200 小时前应是几时? -190 % 12 == 2 是有用处的;-190 % 12 == -10 则是会导致意外的漏洞。

问题7:找到对象的方法和属性

对于一个用户自定义类的实例 x,dir(x) 将返回一个按字母顺序排序的包含实例属性和方法及其类所定义的属性名称的列表

问题8:将字符串和数字相互转换

对于整数,可使用内置的 int() 类型构造器,例如 int('144') == 144。 类似地,可使用 float() 转换为浮点数,例如 float('144') == 144.0。

要将数字144转换为字符串“ 144”,请使用内置类型构造函数str()

问题9:指定位置修改字符串

因为字符串是不可变的。在大多数情况下,您应该简单地从要组装字符串的各个部分中构造一个新字符串。但是,如果您需要一个具有修改就地unicode数据能力的io.StringIO对象,请尝试使用一个对象或array 模块:

import io
s = "Hello, world"
sio = io.StringIO(s)
sio.getvalue() # 'Hello, world'

sio.seek(7) # 返回 7
sio.write("there!") # 返回6
sio.getvalue() # 'Hello, there!

import array
a = array.array('u', s)
print(a) # array('u', 'Hello, world')

a[0] = 'y'
print(a) # array('u', 'yello, world')
a.tounicode() # 'yello, world'

问题10:使用字符串调用函数/方法?

有各种各样的技术。

最好是使用将字符串映射到函数的字典。该技术的主要优点是字符串不需要与函数名称匹配。这也是用于模拟案例构造的主要技术:

def a():
    pass
def b():
    pass

dispatch = {'go': a, 'stop': b}  # 注意 funcs 缺少括号
dispatch['go']()  # 注意调用函数的尾随括号

#使用内置功能getattr():
import foo
getattr(foo, 'bar')()

问题11:高效字符串连接

str和bytes对象是不可变的,因此将许多字符串连接在一起效率不高,因为每次连接都会创建一个新对象。在一般情况下,总运行时成本在总字符串长度中是二次方的。

要累积许多str对象,建议的惯用法是将它们放入列表中并str.join()在最后调用:

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

要累积许多bytes对象,建议的惯用法是bytearray使用就位串联(+=运算符)扩展对象:

result = bytearray()
for b in my_bytes_objects:
    result += b

问题12:元组和列表之间转换

类型构造函数tuple(seq)将任何序列(实际上是任何可迭代的)转换为具有相同顺序的相同项的元组,
把元组转换成列表,使用list函数即可。

问题13:负索引

Python序列用正数和负数索引。对于正数,0是第一个索引,1是第二个索引,依此类推。
对于负索引,-1是最后一个索引,-2是倒数第二个(倒数第二个)索引,依此类推。认为seq[-n]与相同seq[len(seq)-n]。使用负索引可能非常方便。例如S[:-1],除最后一个字符外的所有字符串,这对于从字符串中删除结尾的换行符很有用。

问题14:相反的顺序遍历序列

for x in reversed(sequence):
... # do something with x ..

问题15:从列表中删除重复项

mylist = list(set(mylist))

问题16:根据条件删除列表中多个元素

与删除重复项一样,使用删除条件明确地反向进行迭代是一种可能。但是,使用带隐式或显式正向迭代的切片替换更容易,更快捷。这是三个变体:

mylist[:] = filter(keep_function, mylist)
mylist[:] = (x for x in mylist if keep_condition)
mylist[:] = [x for x in mylist if keep_condition]

问题17:将方法应用于一系列对象

# 使用列表理解:
result = [obj.method() for obj in mylist]

问题18:按另一个列表中的值对一个列表排序

list1 = ["what", "I'm", "sorting", "by"]
list2 = ["something", "else", "to", "sort"]
pairs = sorted(zip(list1, list2))
# [("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
result = [x[1] for x in pairs]
print(result) # ['else', 'sort', 'to', 'something']

问题19: 循环中使用lambda表达式的问题

squares = []
for x in range(5):
    squares.append(lambda: x**2)

这将为您提供包含5个可以计算的lambda的列表x**2。你可能会想到的是,被调用时,他们将返回,分别为0,1, 4,9,和16。但是,当您实际尝试时,您会看到它们全部返回16:

发生这种情况是因为x它不是lambda的局部变量,而是在外部作用域中定义的,并且在调用lambda时将对其进行访问,而不是在定义时进行访问。在循环结束时,值为x is 4,因此所有函数现在都返回4**2,即16。您还可以通过更改的值来验证这一点,x并查看lambda的结果如何变化:

为了避免这种情况,您需要将值保存在lambda局部变量中,以使它们不依赖于global的值x:

squares = []
for x in range(5):
    squares.append(lambda n=x: n**2)

for i in squares:
    print(i())

Python的命名规范

zhangtongle阅读(2836)

前言

Python 玩家在写Python 的时候一定要注意可读性,因为你可能需要二次修改,或者别人使用你的代码,可读性强都是至关重要的,因为Python 是没有Java c 之类的大括号的,一切全靠缩进,如果格式乱,可读性差,那真的是一坨不堪入目的乱码丫。

规范虽然重要,但是先后兼容依然也狠重要,所以在考虑向后兼容的时候,规范可以暂时放到一边,找到一个平衡点就好~。

一般看过我Python自动化办公教程的人都知道,Python万事全靠缩进,缩进是 4个空格,这个数字 “4“,将来你或者你的孩子在参加高考的时候,这个数字可能会出现在考卷上!

课程直达链接!点它,点它,点它:https://edu.51cto.com/sd/b12dd

社区PE8 规范

1、与分隔符对齐。


foo = long_function_name(var_one, var_two,
                         var_three, var_four)

2、添加4个空格以将参数与其余参数区分开。

def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

3、悬挂的缩进应添加一个级别。

foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

4、多条件if 判断

# #没有额外的缩进。
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 添加评论,这将在编辑器中提供一些区别
# 支持语法突出显示。
if (this_is_one_thing and
    that_is_another_thing):
    # 既然这两个条件都成立,那么我们可以断言。
    do_something()

# 在条件延续行上添加一些额外的缩进。
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

5、 多行构造的右花括号/括号/括号可以在列表最后一行的第一个非空白字符下对齐

my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)

6、空格是首选的缩进方法

7、 Python标准库是保守的,需要将行数限制为79个字符(文档字符串/注释数限制为72个)

8、包装长行的首选方法是在括号,方括号和花括号内使用Python的隐含行连续性。

通过将表达式包装在括号中,可以将长行分成多行。应优先使用这些,而不是使用反斜杠进行行连续。
有时反斜杠可能仍然合适。例如,带-语句的long多个不能使用隐式连续,因此可以使用反斜杠:

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

9、运算符在换行之前

# Correct:
# easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)

发现pycharm 好玩的快捷键,ctrl+l 可以跳转到指定行指定字符处。

10、import

# Correct:
import os
import sys
# Correct:
from subprocess import Popen, PIPE

导入应按以下顺序分组:

  1. 标准库导入。

  2. 相关第三方进口。

  3. 本地应用程序/库特定的导入,您应该在每组导入之间放置一个空白行。

  4. 推荐绝对导入

 import mypkg.sibling 

 from mypkg import sibling 

 from mypkg.sibling import example 
  1. 相对导入
 from . import sibling 

 from .sibling import example 

11、 从包含类的模块中导入类时,通常可以这样拼写:

from myclass import MyClass
from foo.bar.yourclass import YourClass
#如果此拼写引起本地名称冲突,则应明确拼写它们:
import myclass
import foo.bar.yourclass
# 并使用" myclass.MyClass"和"foo.bar.yourclass.YourClass"

应该避免使用通配符导入(来自 import *),因为通配符使不清楚名称空间中存在哪些名称,从而使阅读器和许多自动化工具都感到困惑。

通配符导入有一个合理的用例,它是将内部接口重新发布为公共API的一部分(例如,使用可选的加速器模块中的定义覆盖接口的纯Python实现,以及确切的定义将是事先未知)。

12、Python中的单引号和双引号效果是一样的,个人偏好单引号。

13、谨慎使用内联注释。

内联注释是与语句在同一行上的注释。内联注释应与该语句至少分隔两个空格

内联注释是不必要的,并且如果它们表明显而易见,则实际上会分散注意力。不要这样做:

14、文档型字符串

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""

"""Return an ex-parrot."""

15、命名约定

1、首要原则:

对于用户而言,作为API公共部分可见的名称应遵循反映用法而不是实现的约定。

2、命名样式:

Python中使用的不同命名风格包括以下几种。

驼峰式命名法(CamelCase)。

混合式命名法(mixedCase)。

大写(UPPERCASE)或大写加下划线(UPPERCASEWITH_UNDERSCORES)。

前缀(leading)和后缀(trailing)下划线,有时是双下划线(doubled)。

小写元素和大写元素通常是一个单词,有时是几个单词连在一起。使用下划线的通常是缩写短语。使用一个单词要更好一些。前缀和后缀下划线用于标记私有元素和特殊元素。

这些风格被应用到以下几种情形。


 变量。 

 函数和方法。 

 property。 

 类。 

 模块。 

 包。 

CapWords中使用首字母缩写词时,请使用首字母缩写词的所有字母大写。因此,HTTPServerError比HttpServerError好。

3、 还有一种使用短的唯一前缀将相关名称组合在一起的样式

这在Python中使用不多,但是为了完整起见提到它

os.stat()函数返回一个元组,该元组的项目传统上具有诸如st_mode, st_size,st_mtime等名称

*4、_single_leading_underscore:“内部使用”指标较弱。**

例如,从M import *不会导入名称以下划线开头的对象。

5、避免使用的名称

避免使用字母 I ,O, l,.

6、软件包和模块

模块应使用简短的全小写名称, 如果模块名称可以提高可读性,则可以在模块名称中使用下划线。尽管不鼓励使用下划线,但Python软件包也应使用短小写全名

除了特殊模块init之外,模块名称都使用小写,不带下划线。

7、类名

类名通常应使用CapWords约定。

在接口被记录并主要用作可调用函数的情况下,可以代替使用函数的命名约定。

请注意,内置名称有一个单独的约定:大多数内置名称是单个单词(或两个单词一起运行),而CapWords约定仅用于异常名称和内置常量。

类名称始终采用驼峰式命名法,如果它们是模块的私有类,还可能有一个前缀下划线。

类和实例变量通常是名词短语,与用动词短语命名的方法名称构成使用逻辑:

8、类型变量名

在PEP 484中引入的类型变量的名称通常应使用CapWords,而应使用短名称:T,AnyStr,Num。建议将后缀_co或_contra添加到用于分别声明协变或反变行为的变量中:

from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

私有元素 两个前缀下划线,它会在运行时被解释器重命名,以避免与任何子类中的方法产生命名冲突。,

9、异常名称

因为异常应该是类,所以在这里适用类命名约定。但是,您应该在异常名称上使用后缀“ Error”(如果异常实际上是一个错误)。

10、全局变量名

(我们希望这些变量只能在一个模块内使用。)约定与函数的约定大致相同。

11、函数和变量名

函数名称应小写,必要时用下划线分隔单词以提高可读性。

变量名遵循与函数名相同的约定。

12、函数和方法参数

始终将self作为实例方法的第一个参数。

始终对类方法的第一个参数使用cls。

如果函数参数的名称与保留关键字发生冲突,通常最好在其后附加一个下划线,而不要使用缩写或拼写错误。因此,class_优于clss。(也许最好通过使用同义词来避免此类冲突。)

特殊方法

特殊方法(https://docs.python.org/3/reference/datamodel.html#special-method-names)以双下划线开始和结束,常规的方法不应该使用这种约定。有些开发者曾经将其称为dunder方法,作为双下划线(double-underscore)的合成词。它们可用于运算符重载、容器定义等方面。为了保证可读性,它们应该集中放在类定义的开头

参数名称使用小写,如果需要的话可以加下划线。它们遵循与变量相同的命名规则。

13、方法名称和实例变量

使用函数命名规则:小写字母,必要时用下划线分隔单词,以提高可读性。

仅对非公共方法和实例变量使用一个前划线。

为避免名称与子类冲突,请使用两个前导下划线来调用Python的名称处理规则。

Python用类名来修饰这些名称:如果类Foo具有名为a的属性,则Foo . a不能访问它。(坚持的用户仍然可以通过调用Foo._Foo__a来获得访问权限。)通常,双引号下划线仅应用于避免名称与设计为子类的类中的属性发生冲突。

14、常数

常量通常在模块级别定义,并以所有大写字母书写,并用下划线分隔单词。示例包括 MAX_OVERFLOW和TOTAL。

16、编程建议

字符串连接: 用\'\'.join()形式。

与单例(如None)的比较应该始终使用 is或not进行,永远不要使用相等运算符进行

 # Correct:
 if foo is not None: 

17、异常处理

# Correct:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

18、用“has”或“is”前缀命名布尔元素

如果一个元素保存的是布尔值,is和has前缀提供一种自然的方式,使其在命名空间中的可读性更强,代码如下:

  class DB:
      is_connected = False
      has_cache = False

19、用复数形式命名集合变量

如果一个元素保存的是集合变量,那么使用复数形式是一个好主意。有些映射在暴露为序列时也可以从中受益:


  class DB:
      connected_users = ['Tarek']
      tables = {
          'Customer': ['id', 'first_name', 'last_name']
      }

20、用显式名称命名字典

如果一个变量保存的是映射,那么你应该尽可能使用显式名称。例如,如果一个字典保存的是个人地址,那么可以将其命名为persons_addresses,代码如下:

  persons_addresses = {'Bill': '6565 Monty Road',
                       'Pamela': '45 Python street'}
  persons_addresses['Pamela']
  # '45 Python street'

21、避免通用名称

如果你的代码不是在构建一种新的抽象数据类型,那么使用类似list、dict、sequence或elements等专用名词是有害的,即使对于局部变量也一样。它使得代码难以阅读、理解和使用。还应该避免使用内置名称,以避免在当前命名空间中将其屏蔽(shadowing)。还应该避免使用通用的动词,除非它们在该命名空间中有意义。
相反,应该使用领域特定的术语,如下所示:

 def compute(data):  # 太过通用
      for element in data:
          yield element ** 2
  def squares(numbers):  # 更好一些
      for number in numbers:
          yield number ** 2

# 还有一系列前缀和后缀,虽然在编程中非常常见,但事实上应该避免出现在函数和类名称中:
manager;
object;
do、handle或perform。
这样做的原因是它们的含义模糊、模棱两可,且没有向实际名称中添加任何信息

22、避免现有名称

使用上下文中已经存在的名称是不好的做法,因为它会导致阅读代码时——特别是调试时——非常混乱,例如以下代码

23、参数的最佳实践

函数和方法的签名是代码完整性的保证,它们驱动函数和方法的使用并构建其API。

除了我们之前看到的命名规则之外,对参数也要特别小心。这可以通过3个简单的规则来实现。

通过迭代设计构建参数。

信任参数和测试。

小心使用魔法参数*args和**kwargs。

23.1 通过迭代设计构建参数

如果每个函数都有一个固定的、定义明确的参数列表,那么代码的鲁棒性会更好。但这在第一个版本中无法完成,所以参数必须通过迭代设计来构建。它们应该反映创建该元素所针对的使用场景,并相应地逐渐发展。

例如,如果添加了一些参数,它们应该尽可能有默认值,以避免任何退化:

class Service:  # 版本1
   def _query(self, query, type):
      print('done')
   def execute(self, query):
      self._query(query, 'EXECUTE')

>>> Service().execute('my query')
done
import logging
class Service(object):  # 版本2
   def _query(self, query, type, logger):
      logger('done')
   def execute(self, query, logger=logging.info):
      self._query(query, 'EXECUTE', logger)
>>> Service().execute('my query')    # 旧式调用
>>> Service().execute('my query', logging.warning)
WARNING:root:done

23.2 小心使用*args和**kwargs魔法参数

*args和**kwargs参数可能会破坏函数或方法的鲁棒性。它们会使签名变得模糊,而且代码常常在不应该出现的地方构建小型的参数解析器,如下所示:

如果参数列表变得很长而且很复杂,那么添加魔法参数是很吸引人的。但这更表示它是一个脆弱的函数或方法,应该被分解或重构。

如果*args被用于处理元素序列(在函数中以相同方式处理),那么要求传入唯一的容器参数(例如iterator)会更好些,如下所示:

  def fuzzy_thing(**kwargs):
     if 'do_this' in kwargs:
          print('ok i did')
     if 'do_that' in kwargs:
          print('that isdone')
     print('errr... ok')

>>> fuzzy_thing(do_this=1)
ok i did
errr... ok

>>> fuzzy_thing(do_that=1)
that is done
errr... ok

>>> fuzzy_thing(hahaha=1)
errr... ok

如果参数列表变得很长而且很复杂,那么添加魔法参数是很吸引人的。但这更表示它是一个脆弱的函数或方法,应该被分解或重构。

如果*args被用于处理元素序列(在函数中以相同方式处理),那么要求传入唯一的容器参数(例如iterator)会更好些,如下所示

 def sum(*args):  # 可行
      total = 0
      for arg in args:
          total += arg
      return total
  def sum(sequence):  # 更好!
      total = 0
      for arg in sequence:
          total += arg
      return total

 # *kwargs适用于同样的规则。最好固定命名参数,使方法签名更有意义,如下所示:
  def make_sentence(**kwargs):
      noun = kwargs.get('noun', 'Bill')
      verb = kwargs.get('verb', 'is')
      adj = kwargs.get('adjective', 'happy')
      return '%s %s %s' % (noun, verb, adj)

  def make_sentence(noun='Bill', verb='is', adjective='happy'):
      return '%s %s %s' % (noun, verb, adjective)

另一种有趣的方法是创建一个容器类,将多个相关参数分组以提供执行上下文。这种结构与*args或**kwargs不同,因为它可以提供能够操作数值并且能够独立发展的内部构件(internals)。使用它作为参数的代码将不必处理其内部构件。

例如,传入函数的Web请求通常由一个类实例表示。这个类负责保存Web服务器传入的数据,代码如下:

  def log_request(request):  # 版本1
      print(request.get('HTTP_REFERER', 'No referer'))

  def log_request(request):  # 版本2
      print(request.get('HTTP_REFERER', 'No referer'))
      print(request.get('HTTP_HOST', 'No host')

魔法参数有时是无法避免的,特别是在元编程中。例如,想要创建能够处理任何类型签名的函数的装饰器,它是不可或缺的。更普遍地说,在处理对函数进行遍历的未知数据时,魔法参数都很好用,代码如下:

import logging
  def log(**context):
      logging.info('Context is:\n%s\n' % str(context))

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715
error: Sorry,暂时内容不可复制!