Python点点滴滴

Author Avatar
Magicmanoooo 3月 09, 2019
  • 在其它设备中阅读本文章

yield关键字

  1. 在常规的 for...in... 循环之中,后面紧跟的是一个可迭代对象(Iterable),但这类对象在使用时必须将其所有对象都载入内存,但如果有大量的数据,就会十分耗内存
  2. 生成器(Generator)是可以迭代的,但只能使用一次,因为只有在用它的时候才会生成
  3. 生成器能够进行迭代的原因是:它具有一个 next() 方法,工作原理就是通过反复调用 next() 方法来完成迭代,直到捕获一个异常
  4. 带有 yield 关键字的函数不再是一个普通函数,而是一个 generator,可以用于迭代
  5. yield是一个类似return的关键字,迭代一次遇到yield时,就返回yield后面的值。【注】:下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行
  6. yield就是返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后面开始
  7. 带有yield关键字的函数,不只是可以用于for循环,而且可以用于某个函数的参数,只要这个函数的参数允许迭代参数。

OderdedDictpopitem(last=True)

lastTrue时,表示LIFO,即堆栈;反之为FIFO,即队列。

正则表达式

  • \d:可以匹配数字
  • \D:匹配任意非数字
  • \w:可以匹配一个字母或者数字
  • \W:匹配非字母数字及下划线
  • \s:匹配任意空白字符,等价[\t\n\r\f]
  • \S:匹配任意非空字符
  • \A:匹配字符串开始
  • \Z:匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串
  • \z:匹配字符串结束
  • \b:匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b可以匹配never中的er,但不能匹配verb中的er
  • \B: 匹配非单词边界。er\B能匹配verb中的er,但不能匹配never中的er
  • .:可以匹配任意字符(0个或者多个)
  • *:表示任意个字符(包括0个)
  • +:表示至少一个字符(1个或者多个)
  • ?:表示01个字符(匹配0个或者1个由前面的正则表达式定义的片段,非贪婪方式)
  • {n}:表示n个字符
  • {n,m}:表示n~m个字符。例如,0{2,}表示至少匹配2o,即它不能匹配Bob中的o,但能够匹配foooooood中的所有o
  • []表示范围
    • [0-9a-zA-Z\_]:可以匹配到一个数字、字母或者下划线
    • [0-9a-zA-Z\_]+:可以匹配到至少由一个数字、字母或者下划线组成的字符串,例如_num10
    • [a-zA-Z\_][0-9a-zA-Z\_]*:可以匹配到由字母或者下划线开头,后面接任意个有一个数字、字母或者下划线组成的字符串,即identifier
    • [a-zA-Z\_][0-9a-zA-Z\_]{0,19}:相比上面,更加精确地限制了变量的长度为1-20个字符
  • [^...]:表示不再[]中的字符
  • A|B:表示可以匹配AB。例如,(P|p)ython可以匹配Python或者Python
  • ^:表示行的开头。例如,^\d表示必须以数字开头
  • $:表示行的结束。例如,\d$表示必须以数字结束
  • ():表示要提取的分组(Group),即提取子串的能力。【注】:group(0)永远是原始字符串

Python在进行正则匹配时,默认是贪婪匹配,也就是匹配尽可能多的字符。例如,匹配出数字后面的0

re.match(r'^(\d+)(0*)$','102300')

由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*就只能匹配空字符串了。所以,必须要让\d+采用非贪婪匹配(即尽可能少匹配),才能将后面的0匹配出来,加上?就可以让\d+采用非贪婪匹配。

re.match(r'^(\d+?)(0*)$','102300')

re 模块

1. compile()

编译正则表达式模式,返回一个对象的模式(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率)。

格式:

re.compile(pattern, flags=0)

参数:

  • pattern:编译时用的表达式字符串。
  • flags:编译标志位,用于修改正则表达式的匹配方式,如:是否区分大小写,多行匹配等。常用的 flags 有:

例子:

import re
tt = "Tina is a good girl, she is cool, clever, and so on..."
rr = re.compile(r'\w*oo\w*')
print(rr.findall(tt))   #查找所有包含'oo'的单词

# ['good', 'cool']

__repr____str__ 的区别:

__repr____str__ 这两个方法都是用于显示的。其中,__str__是面向用户的,而 __repr__ 是面向程序员的。

区别:

  • 在进行打印操作时,首先会尝试__str__str内置函数(print运行的内部等价形式),它通常返回一个友好的显示
  • __repr__用于所有其他环境中:用于交互模式下,提示回应以及__repr__函数。如果没有使用__str__,会使用printstr。它通常应该返回一个编码字符串,可以用来重建对象,或者给开发者详细的显示。

如果先要所有环境下都同一显示的话,可以重构__repr__方法;当我们想在不同环境下,支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__,已得到更加友好的用户显示。

在进行格式化输出时,%r%s的区别就好比repr()函数处理对象与str()函数处理对象的差别:

  • %s====> str():比较智能
  • %r====>repr():处理较为简单和直接;

对于{!r}这个符号只是在format()之中才有用,其实就是%r

__getattr__

作用:如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败,那么会调用到类的__getattr__函数。如果没有定义这个函数,就会抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步。

例子:

class ObjectDict(dict):
    def __init__(self,*args,**kw):
        super(ObjectDict,self).__init__(*args,**kw)

    def __getattr__(self,name):
        value=self[name]
        if isinstance(value,dict):
            value=ObjectDict(value)
        return value

if __name__=='__main__':
    od=ObjectDict(d={'a':1},f=True)
    print(od.d,od.d.a)

对于上述代码,实现了__getattr__之后,就可以像访问属性一样来访问ObjectDict中的键值对。如果没有实现它,就会报AttributeError: 'ObjectDict' object has no attribute 'd'错误。这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

staticmethod()

返回函数的静态方法,该方法不强制要求传递参数。类可以不用实例化,就可以直接通过class_name.method_name()进行调用。当然,也可以实例化汇总后调用。

IO模块

Python的IO模块主要用于处理三种类型的I/O:文本I/O、二进制I/O以及原始I/O。这些都是通用类型,各种后备设备科使用其中的每一种类型,所以这些类型的具体对象称为文件对象(或者流)

每个具体的流对象都具有各种功能,它可以允许任意的随机访问;向前或者向后寻找任何位置或者只允许顺序访问,如套接字或者pipe的情况。

1、I/O类层次结构

  • I/O inheritance structure的顶部是抽象基类IOBase它定义了流的基本接口,但基本不提供实现,让子类进行继承具体实现。默认实现只是表明一个文件是否为可读、可写或者可寻址(seek)它支持iterator protocol,这意味着可以迭代IOBase对象,从而在一个stream中yielding the lines。根据stream是二进制流(yielding bytes)还是文本流(yielding character strings),lines的定义也略有不同。
    • 对于二进制文件,行终止符(line terminator)总是b\n
    • 对于文本文件,open()的换行参数(newline argument)可以用于选择line terminator
  • RawIOBase继承自IOBase它是raw binary I/O的基类,没有public constructor,主要提供了对底层OS设备或者API的访问(low-level),并且不会将其封装在high-level primitives中)。它的子类为机器文件系统中的文件提供接口。
  • BufferedIOBase继承自IOBase,它是支持某些缓冲区的binary stream的基类,没有公共的构造器。和RawIOBase的区别在于:read()readinto()write()会尽可能(分别)地多读所请求的输入或者尽可能地输出所有给定的输出,但以牺牲多个系统调用为代价。【注】:典型的BufferedIOBase实现不应该从RawIOBase类继承然后实现,而是应该wrap一个RawIOBase
  • TextIOBase继承自IOBase,它是text streams的基类,此类为stream I/O提供基于character和lines的接口。没有提供readinto()方法,因为Python的字符串是不可变的。
  • StringIO是一个in-memory stream for text I/O。在调用close()方法时,将丢弃text buffer。

判断 Python 的版本

可以使用 sys.version_info。因为 version_info 是一个 tuple,其构成为(major,minor,micro,releaselevel,serial)
Python 2.0版本的version_info为:(2, 0, 0, 'final', 0)。或者,可以通过索引来获取各个component,例如,sys.version_info[0]==sys.version_info.major

__str__ VS __unicode__

__str__ 一般是旧式方法,用于返回 bytes。而 __unicode__ 是新式方法,但更加利好,用于返回 characters。虽然名字令人有点儿困惑,但在 2.x 中,出于兼容性的考虑,还是需要坚持使用 __str__。通常,应该将所有字符串格式设置在 __unicode__之中,并创建一个 __str__ 包装方法:

def __str__(self):
    return unicode(self).encode('utf-8')

bytesstr之间的异同

bytes是一种比特流,它的存在形式是 010101111111 这样。无论是在编程或者进行阅读,肯定不会有人直接阅读这种比特流,它必须有一个编码方式,使得其变为有意义的比特流,而不是一堆晦涩难懂的二进制组合。因为编码方式的不同,对这个比特流的解读也不一样,会给实际使用造成很大困扰。

例如:


从上述代码,可以得知,s 是一个 str(即字符串类型)。Python 的内置函数 bytes()可以将字符串 str 转换为 bytes 类型。而 b 实际上是一串二进制组合,为了在 IDE 中让编程人员相对直接地观察,其被表现为 b'\xe4\xb8\xad\xe6\x96\x87' 这种形式。在使用内置函数 bytes() 时,需要明确 encoding 参数,不可省略。

在字符串类str中有一个encode()方法,实现从字符串到比特流的编码过程;而bytes类型拥有decode()方法,实现从比特流向字符串解码过程。这两者几乎拥有一样的函数列表,最大的区别就是上面的两个方法。

本质而言,字符串在磁盘上的保存形式也是二进制组合,也需要编码解码。

两者区别的总结:

  • 在将str(字符串)存入磁盘和从磁盘读取字符串的过程中,Python会自动地帮我们完成编码和解码的工作,我们不需要关心它的过程。
  • 使用bytes类型,实质上是告诉Python,不需要它帮我们自动地完成编码和解码的工作,而是我们自己手动进行,并指定编码格式。
  • Python已经严格区分了bytesstr两种数据类型,我们不能在需要bytes类型参数的时候使用str参数,反之亦然。
  • bytesstr的互相转换过程中,实际就是编码解码的过程,必须显式地指定编码格式。

self

self 名称不是必须的,而且在 Python 中,self 并不是关键字,将其定义成其他名字都可,但约定俗成(为了和其他编程语言同一,减少理解难度)。其次,self 指的是类实例对象本身,而不是类本身。因为如果是指向类本身,那么当创建多个类实例时,self就无法判断指向的是哪一个实例。

u/U""

其表示 Unicode 字符串,可以对字符串进行 Unicode 编码。一般,英文字符在使用各种不同编码的情况下,依旧可以正常解析,所以一般不带 u。但中文就必须需要编码,否则在编码转换时就会出现乱码。

r'xxxx'

如果在前面加r字符,则表示让这个字符串里面的内容失去转义的意义,即得到raw string。

enumerate()

用于将一个Iterable(可遍历)的数据对象(如listtuple或者str)组合成一个索引序列,同时列出数据和数据下标,一般用在for循环中。

函数原型:

enumerate(sequence, [start=0])

其中,sequence表示一个序列、迭代器或者其他支持迭代的对象;start表示下标起始位置。返回enumerate(枚举)对象。

callable()

  • 用于检查一个对象是否是可调用的(可被调用指的是对象能否使用()括号的方法调用)。
  • 可调用对象,在实际调用也可能调用失败;但是不可调用对象,调用肯定不成功。
  • 类对象都是可被调用对象,类的实例对象是否可调用对象,取决于类是否定义了__call__方法。

JSON与XML的区别

至于这两个哪一个更高、更快,其实是没有可比较性的,但是它们的实用范围是不一样的。

例子:

{
  "id": 123,
  "title": "Object Thinking",
  "author": "David West",
  "published": {
    "by": "Microsoft Press",
    "year": 2004
  }
}

相同的内容用XML表示则是:

<?xml version="1.0"?>
<book id="123">
  <title>Object Thinking</title>
  <author>David West</author>
  <published>
    <by>Microsoft Press</by>
    <year>2004</year>
  </published>
</book>

相比较而言,JSON要短一点,并且更加容易理解,在JavaScript中能够完全解析出来。

XML其实不是一种数据格式,而是一种标记语言,

xml.etree.ElementTree模块

XML(eXtensible Markup Language)是指可扩展语言,主要被用于传输和存储数据

XML是一个inherently hierarchical的数据格式,通常用一个tree来表示它。ET有两个类来达到这一目的:

  • ElementTree:将整个XML文档表示为一个tree
  • Element:表示这个tree中的某一个单节点

对于整个document的交互操作(包括读写)通常在ElementTree level,而对于单个XML元素的交互则在Element level。

DOM vs SAX

操作XML有两种方法:DOM和SAX。DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。SAX是流模式,边读边解析,占用内存小,解析快,缺点是需要用户自己处理事件。

正常情况下,优先考虑SAX,因为DOM实在太占内存。

Python处理XML的方法主要有三种:

1. 使用xml.dom.*模块

它是根据W3C DOM API进行实现,很适合处理DOM API。

文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。一个DOM的解析器在解析一个XML文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后便可以利用DOM提供的不同的函数,来读取或修改文档的内容和结构,也可以把修改过的内容写入XML文件

Python中用xml.dom.minidom来解析XML文件。主要步骤为:

  1. 获得子标签
  2. 区分相同标签名的标签
  3. 获取标签的属性值
  4. 获取标签对之间的数据

例子:

#coding=utf-8
import xml.dom.minidom as xmldom
import os

file_path = os.path.abspath("C:/Users/Magicmanoooo/Desktop/demo.xml")
print("文件路径为:",file_path)

# 得到文档对象
dom_object = xmldom.parse(file_path)
print("xmldom.parse:",type(dom_object))

# 得到元素对象
element_object = dom_object.documentElement
print("dom_object.documentElement: ",type(element_object))

# 获得子标签
subelement_object = element_object.getElementsByTagName("object")
print("getElemenstByTagName: ",type(subelement_object))

# 获得标签属性值
#print(subelement_object[0].getAttribute("name"))
subElementObj1 = element_object.getElementsByTagName("name")
for i in range(len(subElementObj1)):
    print ("subElementObj1[%d]:" % i, type(subElementObj1[i]))
    print (subElementObj1[i].firstChild.data)  #显示标签对之间的数据

结果为:

2. 使用 xml.etree.ElementTree 模块

ElementTree 主要就是用于处理 XML,它在 Python 中有两种实现:

  • 纯 Python 实现,如 xml.etree.ElementTree
  • 速度更加快一点儿的 xml.etree.cElementTree

主要步骤为:

  1. 遍历根节点的下一层
  2. 通过下标访问各个标签、属性、文本
  3. 查找 root 下的指定标签
  4. 遍历 XML 文件
  5. 修改 XML 文件
import xml.etree.ElementTree as ET
import os
import sys

def traverse_xml(element):
    if len(element) > 0:
        for child in element:
            print(child.tag, "-----", child.attrib)
            traverse_xml(child)

if __name__ == "__main__":
    file_path = os.path.abspath("C:/Users/Magicmanoooo/Desktop/demo.xml")
    print(file_path)

    try:
        tree = ET.parse(file_path)
        print("tree type: ", type(tree))

        # 获取根节点
        root = tree.getroot()
    except Exception as e:
        print("parse demo.xml fail")
        sys.exit()
    print("root type: ", type(root))
    print(root.tag, "----", root.attrib)

    # 遍历root的下一层
    for child in root:
        print("遍历root的下一层", "-----", child.attrib)

    # 使用下标访问
    #print(root[0].text)

    # 遍历xml文件
    traverse_xml(root)

    # 根据标签名查找root下的所有标签
    name_list = root.findall("item")
    print(len(name_list))
    for name in name_list:
        # print(name.tag, "-----", name.attrib, "-----", name.text)
        print(name.items())

    print(root[0].text)
    print(root[1][1][0].text)

    # 修改xml文件
    login = root.find("login")
    password = login.get("passwd")
    print("before modify: ", password)
    login.set("passwd", "00000")
    print("after modify: ", login.get("passwd"))

    captions = root.findall("caption")
    print(len(captions))
    for caption in captions:
            # print()
        print(caption.tag, "-----", caption.attrib,"-----", caption.text)

使用面向对象的方式:

import xml.etree.ElementTree as ET
import sys
import os.path

class xml_parse:
    def __init__(self, file_path):
        self.tree = None
        self.root = None
        self.file_path = file_path

    def read_xml(self):
        try:
            print("xml file: ", self.file_path)
            self.tree = ET.parse(self.file_path)
            self.root = self.tree.getroot()
        except Exception as e:
            print("parse xml failed")
            sys.exit()
        else:
            print("parse xml success")
        finally:
            return self.tree

    def create_node(self, tag, attrib, text):
        element = ET.Element(tag, attrib)
        element.text = text
        print("tag:%s;attrib:%s;text:%s" % (tag, attrib, text))
        return element

    def add_node(self, parent, tag, attrib, text):
        element = self.create_node(tag, attrib, text)
        if parent:
            parent.append(element)
            element = self.root.find("name")
            print(element.tag, "-----", element.attrib, "-----", element.txt)
        else:
            print("parent is none")

    def write_xml(self, dest_file):
        dest_file = os.path.abspath(dest_file)
        self.tree.write(dest_file, encoding="utf-8",xml_declaration=True)

if __name__ == "__main__":
    file = os.path.abspath("C:/Users/Magicmanoooo/Desktop/demo.xml")
    parse = xml_parse(file)
    tree = parse.read_xml()
    root = tree.getroot()
    print(root)
    parse.add_node(root, "Python", {"age":18, "hello":"world"}, "yes")
    parse.write_xml("C:/Users/Magicmanoooo/Desktop/dest.xml")

使用 xml.sax.* 模块

SAX 是一种基于事件驱动的 API,利用 SAX 解析 XML 涉及到两个部分:

  • 解析器:主要负责读取XML文档,并向事件处理器发送事件。如,元素开始于元素结束事件
  • 事件处理器:负责对事件作出响应,对传递的XML数据进行处理

Python 中使用 SAX 解析 XML 非常简洁,通常需要关心的事件是 start_elementend_elementchar_data

list comprehension(列表推导)

可以更加简洁地创建list,list comprehension由括号组成,括号中包含一个表达式,后跟一个for clause,然后是零个或多个forif子句。 结果将返回一个evaluate后面的forif子句表达式而得到的新list。 例如,如果列表不相等,则此listcomp将两个列表的元素组合在一起:

original_dict = {'a': 1, 'b': 2, 'c': 3}
reversed_dict = dict(
    [(value, key) for (key, value) in original_dict.items()])

# {1: 'a', 2: 'b', 3: 'c'}

join()

用于将序列中的元素以指定的字符连接,生成一个新的字符串。

seq = ("a", "b", "c"); # 字符串序列
print '-'.join( seq );

# a-b-c

str.join(元组、列表、字典、字符串)之后生成的只能是字符串。

enumerate()

用于将一个可遍历的数据对象(如listtuple或字符串)组合成一个索引序列,可同时列出数据和数据下标,一般用在for循环中。其返回enumerate对象。

enumerate(sequence, [start=0])

sequence—— 一个序列、迭代器或其他支持迭代对象
start ——下标起始位置

Python中的***

  • 多个实参,放到一个元组(tuple)中,以*开头,可以传入多个参数
  • **是形参中按照关键字传值,吧多余的传值以字典(dict)的形式呈现

*args:表示将实参按照位置传值,多出来的值都给args,且以tuple的形式呈现

例子:

def foo(x,*args):
    print(x)
    print(args)

foo(1,2,3,4,5) # 其中的2,3,4,5都给了args

# 1
# (2,3,4,5)

args与位置参数和默认参数混用的情况(注意三者的顺序)

例一(三者顺序是:位置参数、默认参数、*args):

def foo(x,y=1,*args):
    print(x)
    print(y)
    print(args)

foo(1,2,3,4,5) # 其中的x为1,y=1的值被2重置了,3,4,5都给了args
 执行结果是:

# 1
# 2
# (3, 4, 5)

例二(三者顺序是:位置参数、*args、默认参数):

def foo(x,*args,y=1):
    print(x)
    print(args)
    print(y)

foo(1,2,3,4,5) # 其中的x为1,2,3,4,5都给了args,y按照默认参数依旧为1

# 1
# (2, 3, 4, 5)
# 1

关于 *,可以从两个角度来看(需要拆分来看):

1. 从形参的角度来看:

例子:

def foo(*args): # 其实这一操作相当于def foo(a,b,c,d,e):
    print(args)
foo(1,2,3,4,5) # 其中的1,2,3,4,5都按照位置传值分别传给了a,b,c,d,e

# (1, 2, 3, 4, 5)

2. 从实参的角度来看:

例子:

def foo(x,y,z):
    print(x)
    print(y)
    print(z)

foo(*(1,2,3)) # 其中的*(1,2,3)拆开来看就是:foo(1,2,3),都按照位置传值分别传给了x,y,z

# 1
# 2
# 3  

`kwargs`:(表示的就是形参中按照关键字传值把多余的传值以字典的方式呈现)**
例子:

def foo(x,**kwargs):
    print(x)
    print(kwargs)
foo(1,y=1,a=2,b=3,c=4) # 将y=1,a=2,b=3,c=4以字典的方式给了kwargs

# {'y': 1, 'a': 2, 'b': 3, 'c': 4}

关于 **kwargs 与位置参数、*args、默认参数混着用的问题:(注意顺序)

1. 位置参数、*args**kwargs三者的顺序必须是:位置参数、*args**kwargs,不然就会报错:

例子:

def foo(x,*args,**kwargs):
    print(x)
    print(args)
    print(kwargs)
foo(1,2,3,4,y=1,a=2,b=3,c=4) # 将1传给了x,将2,3,4以元组方式传给了args,y=1,a=2,b=3,c=4以字典的方式给了kwargs

# 1
# (2, 3, 4)
# {'y': 1, 'a': 2, 'b': 3, 'c': 4}

错误示例:(由于顺序错误)

def foo(x,**kwargs,*args):
    print(x)
    print(args)
    print(kwargs)
foo(1,y=1,a=2,b=3,c=4,2,3,4)

# SyntaxError: invalid syntax

2. 位置参数、默认参数、**kwargs三者的顺序必须是位置参数、默认参数、**kwargs,不然就会报错:

例子:

def foo(x,y=1,**kwargs):
    print(x)
    print(y)
    print(kwargs)
foo(1,a=2,b=3,c=4) # 将1按照位置传值给x,y按照默认参数为1,a=2,b=3,c=4以字典的方式给了kwargs

# 1
# 1
# {'a': 2, 'b': 3, 'c': 4}

关于**,可以从两个角度来看(需要拆分来看):

1. 从形参的角度来看:

例子:

def foo(**kwargs):# 其实就是相当于def foo(y,a,b,c)
    print(kwargs)
foo(y=1,a=2,b=3,c=4)
执行结果是:

s
# {'y': 1, 'a': 2, 'b': 3, 'c': 4}

2. 从实参的角度来看:

例子:

def foo(a,b,c,d):
    print(a)
    print(b)
    print(c)
    print(d)
foo(**{"a":2,"b":3,"c":4,"d":5}) # **{"a":2,"b":3,"c":4,"d":5}是将字典里的每个值按照关键字传值的方式传给a,b,c,d

# 2
# 3
# 4
# 5

例二:

def foo(a,b,c,d=1):
    print(a)
    print(b)
    print(c)
    print(d)
foo(**{"a":2,"b":3,"c":4}) # **{"a":2,"b":3,"c":4}是将字典里的每个值按照关键字传值的方式传给a,b,c;d依旧按照默认参数

# 2
# 3
# 4
# 1

__future__

Python的新版本会引入一些新的功能特性,但一般一部分的新功能可以在旧版本上测试,测试成功再移植到新的版本上,旧版本可以通过导入__future__模块的某些功能,测试新版本的新功能。

例如,在Python 2.x版本中,整数的除法运算,结果是整数,余数被忽略,10/3的结果为3。新版本Python 3.x中,改动的功能除法运算中除法符号为//,结果是整数;符号为/,结果为浮点数。导入新版本的除法运算新功能,通过__future__division实现。

在开头加上from __future__ import print_function这句之后,即使在Python 2.x,使用print就得像Python 3.x那样加括号使用。Python 2.x中print不需要括号,而在Python 3.x中则需要。

gzip

gzip块主要支持打开对应格式的压缩文件,并可以完成对压缩文件的读出和写入操作。压缩文件被打开后,可以使用文件对象一样的方法,如readreadlinereadlineswritewritelines等。

例子:使用gzip模块完成对文件的压缩

f_in = open("data.txt", "rb") # 打开文件
f_out = gzip.open("data.txt.gz", "wb") # 创建压缩文件对象
f_out.writelines(f_in)
f_out.close()
f_in.close()

例子:使用gzip模块完成对文件的解压

f = gzip.open("data.txt.gz", 'rb') # 打开压缩文件对象
f_out=open("data.txt","w") # 打开解压后内容保存的文件
file_content = f.read() # 读取解压后文件内容
f_out.write(file_content.decode("utf-8")) # 写入新文件当中
print(file_content) # 打印读取内容
f.close() # 关闭文件流
f_out.close()

six

six提供了简单的实用程序包来封装Python 2和Python 3之间的差异。它旨在支持无需修改,即可在Python 2和Python 3上工作的代码库。 six只包含一个Python文件,因此无需复制到一个项目中。

urllib模块

1. 基本用法

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

  • url:需要打开的网址
  • dataPost提交的数据
  • timeout:设置网站的访问超时时间

直接用urlopen()获取页面,获得的页面数据格式为bytes类型,需要经过decode()解码,转换成str类型。

利用urlopen()返回的对象,可以使用以下方法对其进行操作:

  • read(),·readline()readlines()fileno()close():对HTTPResponse类型数据进行操作
  • info():返回HTTPMessage对象,表示远程服务器返回的头信息
  • getcode():返回http状态码。如果是http请求,200请求成功完成;404网址未找到
  • geturl():返回请求的url

2. 使用Request

urllib.request.Request(url, data=None, headers={}, method=None)

使用request()来包装请求,再通过urlopen()获取页面。

例子:

url = r'http://www.lagou.com/zhaopin/Python/?labelWords=label'
headers = {
    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
    'Referer': r'http://www.lagou.com/zhaopin/Python/?labelWords=label',
    'Connection': 'keep-alive'
}
req = request.Request(url, headers=headers)
page = request.urlopen(req).read()
page = page.decode('utf-8')

用来包装头部的数据:

  • User-Agent:这个头部可以携带如下几条信息:浏览器名和版本号、操作系统名和版本号、默认语言
  • Referer:可以用来防止盗链,有一些网站图片显示来源http://***.com,就是检查Referer来鉴定的
  • Connection:表示连接状态,记录Session的状态

3. Post数据

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

urlopen()data参数默认为None,当data参数不为空的时候,urlopen()提交方式为Post

例子:

from urllib import request, parse
url = r'http://www.lagou.com/jobs/positionAjax.json?'
headers = {
    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
    'Referer': r'http://www.lagou.com/zhaopin/Python/?labelWords=label',
    'Connection': 'keep-alive'
}
data = {
    'first': 'true',
    'pn': 1,
    'kd': 'Python'
}
data = parse.urlencode(data).encode('utf-8')
req = request.Request(url, headers=headers, data=data)
page = request.urlopen(req).read() # 也可以把data的数据封装在urlopen()参数中
page = page.decode('utf-8')

urllib.parse.urlencode(query, doseq=False, safe='', encoding=None, errors=None)

urlencode()主要作用就是将url附上要提交的数据。

上述的例子中,经过经过urlencode()转换后的data数据为?first=true?pn=1?kd=Python,最后提交的url为:
http://www.lagou.com/jobs/positionAjax.json?first=true?pn=1?kd=Python

Post的数据必须是bytes或者iterable of bytes,不能是str,因此需要进行encode()编码。

异常处理

例子:

def get_page(url):
    headers = {
        'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                    r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',
        'Referer': r'http://www.lagou.com/zhaopin/Python/?labelWords=label',
        'Connection': 'keep-alive'
    }
    data = {
        'first': 'true',
        'pn': 1,
        'kd': 'Python'
    }
    data = parse.urlencode(data).encode('utf-8')
    req = request.Request(url, headers=headers)
    try:
        page = request.urlopen(req, data=data).read()
        page = page.decode('utf-8')
    except error.HTTPError as e:
        print(e.code())
        print(e.read().decode('utf-8'))
    return page

使用代理

urllib.request.ProxyHandler(proxies=None)

当需要抓取的网站设置了访问限制,这时就需要用到代理来抓取数据。

例子:

data = {
        'first': 'true',
        'pn': 1,
        'kd': 'Python'
    }
proxy = request.ProxyHandler({'http': '5.22.195.215:80'})  # 设置proxy
opener = request.build_opener(proxy)  # 挂载opener
request.install_opener(opener)  # 安装opener
data = parse.urlencode(data).encode('utf-8')
page = opener.open(url, data).read()
page = page.decode('utf-8')
return page

zip()

函数原型:

zip([iterable, ...])

用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表

如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用*号操作符,可以将元组解压为列表。

例子:

a = [1, 2, 3]
b = [4, 5, 6]
zipped = zip(a, b)
print(*zipped)

# [(1, 4) (2, 5) (3, 6)]

namedtuple

因为元组具有局限性:不能为元组内部的数据进行命名,所以往往并不知道一个元组所要表达的意义,所以引入了 collections.namedtuple 这个工厂函数,来构造一个带字段名的元组namedtuple 的实例和普通元组消耗的内存一样多,因为字段名都被存在对应的类里面。这个类跟普通的对象实例比起来也要小一些,因为 Python 不会用 __dict__ 来存放这些实例的属性。除了能够用索引来访问数据,能够迭代,还能够方便的通过属性名来访问数据。

namedtuple 对象的定义格式:

collections.namedtuple(typename, field_names, verbose=False, rename=False) 

返回一个 namedtuple 子类 typename。其中:

  • typename:元组名称
  • field_names:元组中元素的名称,是一组 strings。它是有多个字符串组成的可迭代对象,或者是有空格分隔开的字段名组成的字符串
  • rename: 如果元素名称中含有 Python 的关键字,则必须设置为 rename=True

_fields类属性

包含这个类所有字段名的元组

_make(iterable)类方法

接受一个可迭代对象来生产这个类的实例

_asdict() 实例方法

namedtuplecollections.OrdereDict 的形式返回,可以利用它来把元组里的信息友好的展示出来。

例子:

User = namedtuple('User', ['name', 'sex', 'age'])

# 创建一个User对象
user = User(name='Runoob', sex='male', age=12)

# 获取所有字段名
print( user._fields )

# 也可以通过一个list来创建一个User对象,这里注意需要使用"_make"方法
user = User._make(['Runoob', 'male', 12])

print( user )
# User(name='user1', sex='male', age=12)

# 获取用户的属性
print( user.name )
print( user.sex )
print( user.age )

# 修改对象属性,注意要使用"_replace"方法
user = user._replace(age=22)
print( user )
# User(name='user1', sex='male', age=21)

# 将User对象转换成字典,注意要使用"_asdict"
print( user._asdict() )
# OrderedDict([('name', 'Runoob'), ('sex', 'male'), ('age', 22)])

os.path

os.path.abspath(path)

import os  
path1=os.path.abspath('.')   #表示当前所处的文件夹的绝对路径  
path2=os.path.abspath('..')  #表示当前所处的文件夹上一级文件夹的绝对路径  

可以获得当前文件夹的绝对路径。

os.path.exists(path)

如果路径 path 存在,返回 True;反之则返回 False

os.path.split(path)

path 分割成目录和文件名二元组返回。

>>> os.path.split('c:/csv/test.csv') 
# ('c:/csv', 'test.csv') 

>>> os.path.split('c:/csv/') 
# ('c:/csv', '') 

os.path.dirname(path)

返回path的目录(即os.path.split(path)的第一个元素)。

>>> os.path.dirname('c:/csv/test.csv') 
# 'c:/' 

>>> os.path.dirname('c:/csv') 
# 'c:/' 

os.path.basename(path)

返回path最后的文件名。(即os.path.split(path)的第二个元素,如果path\\结尾,那么就会返回空值)。

>>> os.path.basename('c:/test.csv') 
# 'test.csv' 

>>> os.path.basename('c:/csv') 
# 'csv' (这里csv被当作文件名处理了) 

>>> os.path.basename('c:\\csv\\') 
# '' 

os.path.commonprefix(list)

返回list中,所有path共有的最长的路径。

>>>os.path.commonprefix(['/home/td','/home/td/ff','/home/td/fff']) 

# '/home/td' 

os.path.isabs(path)

如果path是绝对路径,则返回True

os.path.isfile(path)

如果path是一个存在的文件,返回True;否则返回False

os.path.isdir(path)

如果path是一个存在的目录,则返回True;否则返回False

os.path.join(path1[, path2[, ...]])

将多个路径组合后返回,第一个绝对路径之前的参数将被忽略。

>>> os.path.join('c:\\', 'csv', 'test.csv') 
# 'c:\\csv\\test.csv' 

>>> os.path.join('windows\temp', 'c:\\', 'csv', 'test.csv') 
# 'c:\\csv\\test.csv' 

>>> os.path.join('/home/aa','/home/aa/bb','/home/aa/bb/c') 
# '/home/aa/bb/c' 

os.path.normcase(path)

LinuxMac平台上,该函数会原样返回path;在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为反斜杠。

>>> os.path.normcase('c:/windows\\system32\\') 
# 'c:\\windows\\system32\\' 

os.path.normpath(path)

规范化路径。

>>> os.path.normpath('c://windows\\System32\\../Temp/') 
# 'c:\\windows\\Temp' 

os.path.splitdrive(path)

返回(drivername,fpath)元组

>>> os.path.splitdrive('c:\\windows') 
# ('c:', '\\windows') 

os.path.splitext(path)

分离文件名与扩展名;默认返回(fname,fextension)元组,可做分片操作 。

>>> os.path.splitext('c:\\csv\\test.csv') 
# ('c:\\csv\\test', '.csv') 

os.path.getsize(path)

返回path的文件的大小(字节)。

>>> os.path.getsize('c:\\boot.ini') 
# 299L 

os.path.getatime(path)

返回 path 所指向的文件或者目录的最后存取时间。

os.path.getmtime(path)

返回 path 所指向的文件或者目录的最后修改时间。

os.getcwd()

用于返回当前工作目录。

os.stat(path)

用于在给定的路径上执行一个系统 stat 的调用。

参数

  • path — 指定路径

返回值

stat 结构:

  • st_mode: inode 保护模式
  • st_ino: inode 节点号
  • st_dev: inode 驻留的设备
  • st_nlink: inode 的链接数
  • st_uid: 所有者的用户 ID
  • st_gid: 所有者的组 ID
  • st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据
  • st_atime: 上次访问的时间
  • st_mtime: 最后一次修改的时间
  • st_ctime: 由操作系统报告的 ctime。在某些系统上(如 Unix)是最新的元数据更改的时间,在其它系统上(如 Windows)是创建时间

例子:

import os,sys

statinfo = os.stat('cifar10_input.py')
print(statinfo)

# os.stat_result(st_mode=33206, 
#                st_ino=4503599627469042, 
#                st_dev=3672258594, 
#                st_nlink=1, 
#                st_uid=0, 
#                st_gid=0, 
#                st_size=1260, 
#                st_atime=1551532715, 
#                st_mtime=1551532715, 
#                st_ctime=1551506243)

if __name__ == '__main__'

1. 通俗的理解__name__ == '__main__'

假如你叫小明.py,在朋友眼中,你是小明(__name__ == '小明');在你自己眼中,你是你自己(__name__ == '__main__')。

if __name__ == '__main__' 的意思是:

  • .py 文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行
  • .py 文件以模块形式被导入时,if __name__ == '__main__' 之下的代码块不被运行

2. 程序入口

对于很多编程语言来说,程序都必须要有一个入口(即 main() 或者Main())。而Python则不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。

一个Python源码文件除了可以被直接运行外,还可以作为模块(也就是库),被其他.py文件导入。不管是直接运行还是被导入,.py文件的最顶层代码都会被运行(Python用缩进来区分代码层次),而当一个.py文件作为模块被导入时,我们可能不希望一部分代码被运行。

2.1 一个.py文件被其他.py文件引用

假设有一个const.py文件,内容如下:

PI = 3.14

def main():
    print("PI:", PI)

main()

# 运行结果:PI: 3.14

现在,写一个用于计算圆面积的area.py文件,area.py文件需要用到const.py文件中的PI变量。从const.py中,把PI变量导入area.py

from const import PI

def calc_round_area(radius):
    return PI * (radius ** 2)

def main():
    print("round area: ", calc_round_area(2))

main()

# PI: 3.14
# round area:  12.56

2.2 修改const.py,添加if __name__ == '__main__'

可以看到const.py中的main函数也被运行了,实际上不希望它被运行,因为const.py提供的main函数只是为了测试常量定义。这时if __name__ == '__main__'派上了用场。把const.py改一下,添加if __name__ == '__main__'

PI = 3.14

def main():
    print("PI:", PI)

if __name__ == "__main__":
    main()

运行const.py,输出如下:

PI: 3.14

运行area.py,输出如下:

round area:  12.56

可以看到if __name__ == '__main__'相当于Python模拟的程序入口,Python本身并没有这么规定,这只是一种编码习惯。由于模块之间相互引用,不同模块可能有这样的定义,而程序入口只有一个。到底哪个程序入口被选中,这取决于__name__的值。

3. __name__

3.1 __name__反映一个包的结构

__name__是内置变量,可用于反映一个包的结构。假设有一个包a,包的结构如下:

在包a中,文件c.py__init__.py__init__.py的内容都为:

print(__name__)

当一个.py文件(模块)被其他.py文件(模块)导入时,在命令行执行

Python -c "import a.b.c"

# a
# a.b
# a.b.c

由此可见,__name__可以清晰地反映一个模块在包中的层次。

3.2 __name__表示当前模块的名字

__name__是内置变量,可用于表示当前模块的名字。直接运行一个.py文件(模块)

Python a/b/c.py
# __main__

由此可知:如果一个.py文件(模块)被直接运行时,则其没有包结构,其__name__值为__main__,即模块名为__main__

所以,if __name__ == '__main__'的意思是:

  • .py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行
  • .py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行

4. __main__.py文件与Python -m

Python的-m参数用于将一个模块或者包作为一个脚本运行,而__main__.py文件相当于是一个包的“入口程序“。

4.1 运行Python程序的两种方式

  • Python xxx.py,直接运行xxx.py文件
  • Python -m xxx.py,把xxx.py当做模块运行

假设有一个文件run.py

import sys
print(sys.path)

用直接运行的方式启动

Python run.py
# ['/home/huoty/aboutme/Pythonstudy/main', ...] 输出结果只截取了重要部分

然后以模块的方式运行:

Python -m run.py
# ['', ...]
# /usr/bin/Python: No module named run.py

由于输出结果只列出了关键的部分,应该很容易看出他们之间的差异:

  • 直接运行方式是把run.py文件所在的目录放到了sys.path属性中
  • 以模块方式运行是把你输入命令的目录(也就是当前工作路径),放到了sys.path属性中。

以模块方式运行还有一个不同的地方:多出了一行No module named run.py的错误。实际上以模块方式运行时,Python先对run.py执行一遍import,所以print(sys.path)被成功执行,然后Python才尝试运行run.py模块,但是在path变量中并没有run.py这个模块,所以报错。正确的运行方式,应该是Python -m run

4.2 __main__.py的作用

假设有如下一个包package:

其中,文件__init__.py的内容:

import sys

print("__init__")
print(sys.path)

文件__main__.py的内容`:

import sys

print("__main__")
print(sys.path)

接下来,运行这个package(使用Python -m package)运行,输出结果:

__init__
['', ...]

__main__
['', ...]

使用Python package运行,输出结果:

__main__
['package', ...]

总结:

  • 当加上-m参数时,Python会把当前工作目录添加到sys.path中;而不加-m时,Python则会把脚本所在目录添加到sys.path中。
  • 当加上-m参数时,Python会先将模块或者包导入,然后再执行。
  • __main__.py文件是一个包或者目录的入口程序。不管是用Python package还是用Python -m package运行,__main__.py文件总是被执行。

argparse

1. 创建解析器:ArgumentParser

import argparse
parser = argparse.ArgumentParser()
class ArgumentParser(
    prog=None, 
    usage=None, 
    description=None, 
    epilog=None, 
    parents=[],             
    formatter_class=argparse.HelpFormatter, 
    prefix_chars='-', 
    fromfile_prefix_chars=None, 
    argument_default=None, 
    conflict_handler='error', 
    add_help=True)

创建一个 ArgumentParser 实例,ArgumentParser 的参数都为关键字参数。

  • prog文件名,默认为 sys.argv[0],用来在 help信息中描述程序的名称。
  • usage描述程序用途的字符串
  • descriptionhelp 信息前显示的信息
  • epiloghelp信息之后显示的信息

  • parentsArgumentParser 对象组成的列表,它们的 arguments 选项会被包含到新 ArgumentParser 对象中。(类似于继承)

  • formatter_classhelp 信息的输出的格式
  • prefix_chars参数前缀,默认为-(最好不要修改)

  • fromfile_prefix_chars前缀字符,放在文件名之前。当参数过多时,可以将参数放到文件中读取,例子中 parser.parse_args(['-f','foo', '@args.txt'])解 析时会从文件 args.txt 读取,相当于 ['-f', 'foo', '-f', 'bar']

  • conflict_handler解决冲突的策略,默认情况下冲突会发生错误,(最好不要修改)
  • add_help是否增加 -h/-help 选项 (默认为 True),一般 help 信息都是必须的。设为 False 时,help信息里面不再显示 -h –help 信息
  • argument_default(default: None)设置一个全局的选项的缺省值,一般每个选项单独设置,基本没用

2. 添加参数选项:add_argument

ArgumentParser.add_argument(
    name or flags...
    [, action]
    [, nargs]
    [, const]
    [, default]
    [, type]
    [, choices]
    [, required]
    [, help]
    [, metavar]
    [, dest])
  • name or flags:参数有两种,可选参数和位置参数。
    • 添加可选参数
      parser.add_argument('-f', '--foo')
      
    • 添加位置参数(解析时缺少位置参数就会报错了)
      parser.add_argument('bar')
      
      parse_args() 运行时,默认会用 - 来认证可选参数,剩下的即为位置参数。

  • action:默认为 store

    • store_const:值存放在 const 中:
    • store_truestore_false:值存为 TrueFalse
    • append:存为列表,可以有多个参数
    • append_const:存为列表,会根据const关键参数进行添加:
    • count:统计参数出现的次数
    • helphelp信息
    • version:版本
  • metaver:帮助信息中显示的参数名称
  • nargs: 参数的数量
    • 值可以为整数 NN 个),*(任意多个,可以为 0 个),+(一个或更多)
    • 值为 ? 时,首先从命令行获得参数,如果有 -y后面没加参数,则从 const 中取值;如果没有 -y,则从 default 中取值
  • const:保存一个常量
  • default:默认值
  • type:参数类型,默认为str
  • choices:设置参数值的范围,如果 choices中的类型不是字符串,记得指定type
  • required:该选项是否必选,默认为True
  • dest:参数名

3. 解析参数

像名称空间一样使用即可。

re

ArgumentParser.parse_known_args(args=None, namespace=None)

有时,脚本只能解析一些命令行参数,将剩余的参数传递给另一个脚本或程序。 在这些情况下,可以使用 parse_known_args()。 它的工作方式与 parse_args() 非常相似,只是在存在额外参数时不会产生错误。 相反,它返回一个包含填充命名空间(namespace)和剩余参数字符串 list 的两项 tuple

vars([object])

返回对象 object 的属性和属性值的字典对象。如果没有参数,就打印当前调用位置的属性和属性值,类似 locals()

sys模块

  • os 模块提供了一种方便的使用操作系统函数的方法。
  • sys 模块可供访问由解释器使用或维护的变量和与解释器进行交互的函数。

总之,os 模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;sys 模块负责程序与 Python 解释器的交互,提供了一系列的函数和变量,用于操控 Python 的运行时环境。

sys.modules

sys.modules 是一个全局字典,该字典在 Python 启动后就加载在内存中。每当导入新的模块时,sys.modules 都将记录这些模块。字典 sys.modules 对于加载模块起到了缓冲的作用。当某个模块第一次导入,字典 sys.modules 将自动记录该模块。当第二次再导入该模块时,Python 会直接到字典中查找,从而加快了程序运行的速度。

字典 sys.modules 具有字典所拥有的一切方法,可以通过这些方法了解当前的环境加载了哪些模块。

sys.modules 返回所有已经导入的模块列表 :

  • keys():返回模块名
  • values()`:返回模块
  • modules([module_name]):返回路径

sys.stdoutsys.stdin

如果需要更好的控制输出,而 print 不能满足需求,sys.stdoutsys.stdinsys.stderr 的功能更强大。

1. sys.stdoutprint

在 Python 中调用 print 时,事实上调用了 sys.stdout.write(obj+'\n')print 将需要的内容打印到控制台,然后追加一个换行符。

以下两行代码等价:

sys.stdout.write('hello' + '\n')
print('hello')

2. sys.stdininput

sys.stdin.readline() 会将标准输入全部获取,包括末尾的 '\n',因此用 len 计算长度时是把换行符 '\n' 算进去了的,但是 input() 获取输入时,返回的结果是不包含末尾的换行符 '\n' 的。

因此如果在平时使用 sys.stdin.readline() 获取输入的话,不要忘了去掉末尾的换行符,可以用 strip() 函数(sys.stdin.readline( ).strip('\n'))或 sys.stdin.readline()[:-1] 这两种方法去掉换行。

import sys 
v1 = input()
v2 = sys.stdin.readline()
print(len(v1))
print(len(v2))

3. 从控制台重定向到文件

原始的 sys.stdout 指向控制台,如果把文件的对象引用赋给 sys.stdout,那么 print 调用的就是文件对象的 write 方法。

glob

文件名模式匹配,不用遍历整个目录判断每个文件是不是符合。

1. 通配符

星号(*)匹配零个或多个字符。

import glob

for name in glob.glob('dir/*')
    print(name)

# dir/file.txt
# dir/file1.txt
# dir/file2.txt
# dir/filea.txt
# dir/fileb.txt
# dir/subdir

列出子目录中的文件,必须在模式中包括子目录名:

import glob

# 用子目录查询文件
print('Named explicitly:')
for name in glob.glob('dir/subdir/*'):
    print('\t', name)

# 用通配符*代替子目录名
print('Named with wildcard:')
for name in glob.glob('dir/*/*'):
    print ('\t', name)

# Named explicitly:
#         dir/subdir/subfile.txt
# Named with wildcard:
#        dir/subdir/subfile.txt

2. 单个字符通配符

用问号(?)匹配任何单个的字符。

for name in glob.glob('dir/file?.txt'):
    print(name)

# dir/file1.txt
# dir/file2.txt
# dir/filea.txt
# dir/fileb.txt

3. 字符范围

当需要匹配一个特定的字符,可以使用一个范围。

for name in glob.glob('dir/*[0-9].*'):
    print(name)

# dir/file1.txt
# dir/file2.txt

Numpy.random.shuffle VS Numpy.random.permutation

shufflepermutation 都是对原来的数组进行重新洗牌(即随机打乱原来的元素顺序)。

区别在于:

  • shuffle:直接在原来的数组上进行操作,改变原来数组的顺序,无返回值。
  • permutation:不直接在原来的数组上进行操作,而是返回一个新的打乱顺序的数组,并不改变原来的数组。

例子:

a = np.arange(12)
print(a)
print('\n')

np.random.shuffle(a)
print(a)
print('\n')

a = np.arange(12)
print(a)
print('\n')

b = np.random.permutation(a)
print(b)
print(a)

# [ 0  1  2  3  4  5  6  7  8  9 10 11]

# [ 3  1 10  4  2 11  0  9  6  5  7  8]

# [ 0  1  2  3  4  5  6  7  8  9 10 11]

# [ 7  0  4  1  9  5  2 10 11  6  8  3]
# [ 0  1  2  3  4  5  6  7  8  9 10 11]

np.load

numpy.load(file, 
           mmap_mode=None, 
           allow_pickle=True, 
           fix_imports=True, 
           encoding='ASCII')

返回:arraytuple 或者 dict

例子:

np.load(vgg16_npy_path, encoding='latin1').item()
'''
np.load(vgg16_npy_path, encoding='latin1')是一个dict类型,将其转换为(key,value)形式
'''

numpy 提供了便捷的内部文件存取,将数据存为np 专用的 npy(二进制格式)或 npz(压缩打包格式)格式。

对各个格式文件的存取如下:

  • np 格式:np.savenp.load
  • znp 格式:np.saveznp.load
  • csv 文件:np.savetxtnp.loadtxt

切片操作

a='Python'
b=a[::-1]
print(b) #nohtyp
c=a[::-2]
print(c) #nhy
#从后往前数的话,最后一个位置为-1
d=a[:-1]  #从位置0到位置-1之前的数
print(d)  #pytho
e=a[:-2]  #从位置0到位置-2之前的数
print(e)  #pyth

用法说明

b = a[i:j]   # 表示复制a[i]到a[j-1],以生成新的list对象

a = [0,1,2,3,4,5,6,7,8,9]
b = a[1:3]   # [1,2]
'''
当i缺省时,默认为0,即 a[:3]相当于 a[0:3]
当j缺省时,默认为len(alist), 即a[1:]相当于a[1:10]
当i,j都缺省时,a[:]就相当于完整复制一份a
'''

b = a[i:j:s]表示:i,j与上面的一样,但s表示步进,缺省为1.
'''
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1

所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。
'''

Python 带 _ 的变量或函数命名

Python 中的标识符可以包含数字、字母和 _,但必须以字母或者 _ 开头,其中以 _ 开头的命名一般具有特殊的意义。

1. 前后均带有双下划线 __ 的命名

一般用于特殊方法的命名,用来实现对象的一些行为或者功能,比如 __new__() 方法用来创建实例,__init__() 方法用来初始化对象,x + y 操作被映射为方法 x.__add__(y),序列或者字典的索引操作 x[k] 映射为 x.__getitem__(k)__len__()__str__() 分别被内置函数 len()str() 调用等等。

2. 仅开头带双下划线 __ 的命名

用于对象的数据封装,以此命名的属性或者方法为类的私有属性或者私有方法。

`class` `Foo(``object``):``    ``def` `__init__(``self``):``        ``self``.__name ``=` `'private name'` `    ``def` `getname(``self``):``        ``return` `self``.__name` `    ``def` `__spam(``self``):``        ``print` `'private method'` `    ``def` `bar(``self``):``        ``self``.__spam()`

在外部直接访问私有属性或者方法是不可行的,这就起到了隐藏数据的作用:

`>>> f ``=` `Foo()``>>> f.__name` `Traceback (most recent call last):``  ``File` `"<pyshell#1>"``, line ``1``, ``in` `<module>``    ``f.__name``AttributeError: ``'Foo'` `object` `has no attribute ``'__name'``>>> f.__spam()` `Traceback (most recent call last):``  ``File` `"<pyshell#2>"``, line ``1``, ``in` `<module>``    ``f.__spam()``AttributeError: ``'Foo'` `object` `has no attribute ``'__spam'`

但是这种实现机制并不是很严格,机制是通过自动”变形”实现的,类中所有以双下划线开头的名称 __name 都会自动变为 _类名__name 的新名称:

`>>> f._Foo__name``'private name'``>>> f._Foo__spam()``private method`

这样就可以访问了。这种机制可以阻止继承类重新定义或者更改方法的实现,比如,定义一个 Foo 的派生类:

`class` `Goo(Foo):``    ``def` `__spam(``self``):``        ``print` `'private method of Goo'`

重写了 __spam 方法,运行:

`>>> g ``=` `Goo()``>>> g.bar()``private method`

调用 bar() 方法的时候依然执行的是 Foo 类的 __spam() 方法,因为在 bar() 方法的实现中,self.__spam() 已自动变形为 self._Foo__spam()Goo 继承的 bar() 方法也是如此。

3. 以单下划线 _ 开头的命名

一般用于模块中的”私有”定义的命名。from module import * 语句用于加载模块中的所有名称,要控制导入的名称,一种方法是定义列表 __all__,只有在 __all__ 中的名称才能通过 * 导入;另一种方法就是以单下划线开头命名定义了,这种定义不会被 * 导入。

当然,在类中也可以用单下划线开头来命名属性或者方法,这只是表示类的定义者希望这些属性或者方法是”私有的”,但实际上并不会起任何作用。

tarfile 模块

1. 打包及重命名文件

import tarfile
# 以w模式创建文件
tar = tarfile.open('tar_file.tar','w')
# 添加一个文件,arcname可以重命名文件
tar.add('/tmp/folder/file.txt', arcname='file.log')
# 添加一个目录
tar.add('/tmp/folder/tmp')                         
# 关闭
tar.close()

2. 查看文件列表

tar = tarfile.open('tar_file.tar','r')             
# 获取包内的所有文件列表
tar.getmembers()
[<TarInfo 'file.log' at 0x7f737af2da70>, <TarInfo 'tmp/folder/tmp' at 0x7f737af2dd90>]

3. 追加

# 以a模式创建文件
tar = tarfile.open('tar_file.tar','a')
tar.add('/tmp/folder/sc.pyc')
tar.close()
tar = tarfile.open('tar_file.tar','r')
tar.getmembers()
[<TarInfo 'file.log' at 0x7ff8d4fa1110>, <TarInfo 'tmp/folder/tmp' at 0x7ff8d4fa11d8>, <TarInfo 'tmp/folder/sc.pyc' at 0x7ff8d4fa12a0>]

4. 解压全部文件

import tarfile
tar = tarfile.open('tar_file.tar','r')
tar.extractall()
tar.close()

5. 解压单个文件

如果我们的压缩包很大的情况下,就不能够一次性解压了,那样太耗内存了,可以通过下面的方式进行解压,其原理就是一个文件一个文件的解压。

import tarfile
tar = tarfile.open('tar_file.tar','r')
for n in tar.getmembers():
    tar.extract(n,"/tmp")
tar.close()

6. 压缩一个文件夹下的所有文件

#coding=utf8  
import os  
import tarfile  

__author__ = 'Administrator'  

def main():  
    cwd = os.getcwd()  
    tar = tarfile.open('test.tar','w:gz')  
    for root ,dir,files in os.walk(cwd):  
        for file in files:  
            fullpath = os.path.join(root,file)  
            tar.add(fullpath)  

if __name__=='__main__':  
    main()  

urllib 模块

1. urllib.urlopen(url[,data[,proxies]])

打开一个 url 的方法,返回一个文件对象,然后可以进行类似文件对象的操作。【注意】对于 Python3.X,应该用 urllib.request,否则话会报错:AttributeError: module 'urllib' has no attribute 'urlopen'

# 本例试着打开 baidu
import urllib.request

f = urllib.request.urlopen('http://www.baidu.com/')
first_line = f.readline()
print(first_line)

# b'<!DOCTYPE html>\n'

urlopen 返回对象提供方法:

  • read()readline()readlines()fileno()close():这些方法的使用方式与文件对象完全一样
  • info():返回一个 httplib.HTTPMessage 对象,表示远程服务器返回的头信息
  • getcode():返回 Http 状态码。如果是 http 请求,200 请求成功完成;404 网址未找到
  • geturl():返回请求的 url

2. urllib.urlretrieve(url[,filename[,reporthook[,data]]])

url 定位到的 html 文件下载到你本地的硬盘中。如果不指定 filename,则会存为临时文件。

urlretrieve() 返回一个二元组 (filename, mine_hdrs)。【注意】:Python2 与 Python3 的 urllib 不同在与 Python3 要加上 .request

临时存放:

import urllib.request

f = urllib.request.urlretrieve('https://www.baidu.com/')
print(type(f))
# <class 'tuple'>

print(f[0])
# C:\Users\MAGICM~1\AppData\Local\Temp\tmp32cx296x

print(f[1])
# Accept-Ranges: bytes
# Cache-Control: no-cache
# Content-Length: 227
# Content-Type: text/html
# Date: Sat, 02 Mar 2019 13:58:32 GMT
# Etag: "5c73a860-e3"
# Last-Modified: Mon, 25 Feb 2019 08:33:36 GMT
# P3p: CP=" OTI DSP COR IVA OUR IND COM "
# Pragma: no-cache
# Server: BWS/1.1
# Set-Cookie: BD_NOT_HTTPS=1; path=/; Max-Age=300
# Set-Cookie: BIDUPSID=66D7A90F912A514E83F04FC1CA386389; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
# Set-Cookie: PSTM=1551535112; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
# Strict-Transport-Security: max-age=0
# X-Ua-Compatible: IE=Edge,chrome=1
# Connection: close

存为本地文件:

>>> filename = urllib.request.urlretrieve('http://www.google.com.hk/',filename='/home/dzhwen/Python文件/Homework/urllib/google.html')
>>> type(filename)
<type 'tuple'>
>>> filename[0]
'/home/dzhwen/Python\xe6\x96\x87\xe4\xbb\xb6/Homework/urllib/google.html'
>>> filename[1]
<httplib.HTTPMessage instance at 0xb6e2c38c>

3. urllib.urlcleanup()

清除由于 urllib.urlretrieve() 所产生的缓存。

4. urllib.quote(url)urllib.quote_plus(url)

url 数据获取之后,并将其编码,从而适用与 url 字符串中,使其能被打印和被 WEB 服务器接受。

import urllib.request

print(urllib.request.quote('http://www.baidu.com'))
# http%3A//www.baidu.com

5. urllib.unquote(url)urllib.unquote_plus(url)

与 4 的函数相反。

6. urllib.urlencode(query)

url 中的键值对以连接符 & 划分,这里可以与 urlopen 结合以实现 post 方法和 get 方法(【注意】:Python3 中的需要这样调用:urllib.parse.urlencode):

GET 方法:

import urllib.request


params = urllib.parse.urlencode({'spam':1,'eggs':2,'bacon':0})
print(params)
# spam=1&eggs=2&bacon=0

POST 方法:

import urllib.request

parmas = urllib.parse.urlencode({'about':1, 'quotes':2})
f = urllib.request.urlopen("http://Python.org", parmas)
print(f.read())

cPickle

在 Python 中,一般可以使用 pickle 类来进行 Python 对象的序列化,而 cPickle 提供了一个更快速简单的接口,如 Python 文档所说的:“cPickle – A faster pickle”。

cPickle 可以对任意一种类型的 Python 对象进行序列化操作,比如 listdict,甚至是一个类的对象等。而所谓的序列化,就是为了能够完整的保存并能够完全可逆的恢复。在 cPickle 中,主要有四个函数可以做这一工作。

1. dump: 将 Python 对象序列化保存到本地的文件

import cPickle

data = range(1000)
cPickle.dump(data,open("test\\data.pkl","wb"))

dump 函数需要指定两个参数,第一个是需要序列化的 Python 对象名称,第二个是本地的文件。需要注意的是,在这里需要使用 open 函数打开一个文件,并指定“写”操作 。

2. load:载入本地文件,恢复 Python 对象

data = cPickle.load(open("test\\data.pkl","rb")) 

dump 一样,这里需要使用 open 函数打开本地的一个文件,并指定“读”操作。

3. dumps:将 Python 对象序列化保存到一个字符串变量中

data_string = cPickle.dumps(data)

4. loads:从字符串变量中载入 Python 对象

data = cPickle.loads(data_string)

5. pickle 模块使用的数据格式是 Python 专用的,并且不同版本不向后兼容,同时也不能被其他语言说识别。要和其他语言交互,可以使用内置的 json

使用 pickle 模块你可以把 Python 对象直接保存到文件,而不需要把它们转化为字符串,也不用底层的文件访问操作把它们写入到一个二进制文件里。 pickle 模块会创建一个 Python 语言专用的二进制格式,基本上不用考虑任何文件细节,它会帮你干净利落地完成读写独享操作,唯一需要的只是一个 合法的文件句柄。

pickle 模块中的两个主要函数是 dump()load()dump() 函数接受一个文件句柄和一个数据对象作为参数,把数据对象以特定的格式保存 到给定的文件中。当使用 load() 函数从文件中取出已保存的对象时,pickle 知道如何恢复这些对象到它们本来的格式。

dumps() 函数执行和 dump() 函数相同的序列化。取代接受流对象并将序列化后的数据保存到磁盘文件,这个函数简单的返回序列化的数据。

loads() 函数执行和 load() 函数一样的反序列化。取代接受一个流对象并去文件读取序列化后的数据,它接受包含序列化后的数据的 str 对象, 直接返回的对象。

cPicklepickle 得一个更快得 C 语言编译版本。picklecPickle 相当于 Java 的序列化和反序列化操作。

if __name__ == "__main__":
    import cPickle

#序列化到文件
obj = 123,"abcdedf",["ac",123],{"key":"value","key1":"value1"}
print(obj)
# 输出:(123, 'abcdedf', ['ac', 123], {'key1': 'value1', 'key': 'value'})
# r+ 读写权限 r+b 读写到二进制文件

f = open(r"d:\a.txt","r+")
cPickle.dump(obj,f)
f.close()
f = open(r"d:\a.txt")
print(cPickle.load(f))
# 输出:(123, 'abcdedf', ['ac', 123], {'key1': 'value1', 'key': 'value'})

# 序列化到内存(字符串格式保存),然后对象可以以任何方式处理如通过网络传输
obj1 = cPickle.dumps(obj)
print(type(obj1))
#输出:<type 'str'>
print(obj1)
# 输出:Python专用的存储格式

obj2 = cPickle.loads(obj1)
print(type(obj2))
# 输出:<type 'tuple'>

print(obj2)
#输出:(123, 'abcdedf', ['ac', 123], {'key1': 'value1', 'key': 'value'})

np.concatenate 函数

numpy.concatenate((a1, a2, ...), axis=0)

Join a sequence of arrays along an existing axis.(按轴 axis 连接 array 组成一个新的 array)。

The arrays must have the same shape, except in the dimension corresponding to axisaxis:default is 0

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])              # b是一个二维array
np.concatenate((a, b), axis=0)
# array([[1, 2],
#       [3, 4],
#       [5, 6]])

np.concatenate((a, b.T), axis=1)

# array([[1, 2, 5],
#       [3, 4, 6]])


b = np.array([[5,6]])      # 可以看出b是二维的不是一维的
print(b.shape)
# (1, 2)

b = np.array([5,6])
print(b.shape)
# (2,)

更普通的例子:

a = np.array([[1, 2], [3, 4]])                # a、b的shape为(2,2),连接第一维就变成(4,2),连接第二维就变成(2,4)
b = np.array([[5, 6], [7, 8]])
np.concatenate((a,b),axis=0)
# array([[1, 2],
#        [3, 4],
#        [5, 6],
#        [7, 8]])

np.concatenate((a,b),axis=1)
# array([[1, 2, 5, 6],
#        [3, 4, 7, 8]])

c = np.concatenate((a,b),axis=1)
# array([[1, 2, 5, 6],
#        [3, 4, 7, 8]])

print(c.shape)
# (2, 4)

concatenate([a, b]) 连接,连接后 ndim 不变,ab 可以有一维 size 不同,但 size 不同的维度必须是要连接的维度。

例如,a.shape(4,5,6,10)b.shape(4,5,6,20)

np.concatenate([a,b], axis=3) 
# 返回张量的shape为(4,5,6,30)
a=np.array([1,2,3])
b=np.array([11,22,33])
c=np.array([44,55,66])
np.concatenate((a,b,c),axis=0)  
# array([ 1,  2,  3, 11, 22, 33, 44, 55, 66]) 

Numpy数字转置的三种转置方法

1. 数组转置 T

创建二维数组 data 如下:

import numpy as np
data = np.arange(10).reshpe(2, 5)
print(data)
# [[0 1 2 3 4]
# [5 6 7 8 9]]

print(data.T)
# [[0 5]
#  [1 6]
#  [2 7]
#  [3 8]
#  [4 9]]

2. 轴对换之 transpose

对于高维数组,可以使用轴对换来对多个维度进行变换。

data = np.arange(24).reshape(2, 3, 4)
print(data)

# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]

#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

print(data.transpose(1, 0 , 2))
# [[[ 0  1  2  3]
#   [12 13 14 15]]

#  [[ 4  5  6  7]
#   [16 17 18 19]]

#  [[ 8  9 10 11]
#   [20 21 22 23]]]

transpose 进行的操作其实是将各个维度重置,原来 (2,3,4) 对应的是 (0,1,2)。使用 transpose(1,0,2) 后,各个维度大小变为 (3,2,4),其实就是将第一维和第二维互换。

对于这个三维数组,转置T其实就等价于 transpose(2,1,0),如下:

print(data.transpose(2, 1, 0))
# [[[ 0 12]
#   [ 4 16]
#   [ 8 20]]

#  [[ 1 13]
#   [ 5 17]
#   [ 9 21]]

#  [[ 2 14]
#   [ 6 18]
#   [10 22]]

#  [[ 3 15]
#   [ 7 19]
#   [11 23]]]

3. 两轴对换 swapaxes

swapaxes 方法接受的参数是一对轴编号,使用 transpose 方法是对整个轴进行对换,而 swapaxes 是将参数的两个轴进行对换。

刚刚上面的 transpose(1,0,2),实际上就是将 01 轴进行对换,因此使用 swapaxes 也可以实现,如下:

print(data.swapaxes(0, 1))

# [[[ 0  1  2  3]
#   [12 13 14 15]]

#  [[ 4  5  6  7]
#   [16 17 18 19]]

#  [[ 8  9 10 11]
#   [20 21 22 23]]]

reshape

参数:

  • a:数组—需要处理的数据。
  • newshape:新的格式—整数或整数数组,如 (2,3) 表示 23 列。新的形状应该与原来的形状兼容,即行数和列数相乘后等于 a 中元素的数量。如果是整数,则结果将是长度的一维数组,所以这个整数必须等于 a 中元素数量。若这里是一个整数数组,那么其中一个数据可以为 -1。在这种情况下,这个个值 Python 会自动从根据第二个数值和剩余维度推断出来。
  • order:可选范围—{'C', 'F', 'A'}。使用索引顺序读取 a 的元素,并按照索引顺序将元素放到变换后的的数组中(如果不进行 order 参数的设置,默认为 'C'。)。
    • 'C' 指的是用类 C 写的读/索引顺序的元素,横着读,横着写,优先读/写一行。
    • 'F' 是指用 FORTRAN 类索引顺序读/写元素,竖着读,竖着写,优先读/写一列。【注意,'C''F' 选项不考虑底层数组的内存布局,只引用索引的顺序】
    • 'A' 竖着读,横着写。

示例(有两种使用方法,可以使用 np.reshape(r, (-1,1), order='F'),也可以使用 r1 = p.reshape((-1,1), order='F')):

import numpy as np

r = np.arange(12)
r1 = r
r2 = r
r3 = r
r1 = r1.reshape((3,4), order='F')
# print(r1)
# [[ 0  3  6  9]
#  [ 1  4  7 10]
#  [ 2  5  8 11]]

r2 = r2.reshape((3,4), order='A')
# print(r2)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

r3 = r3.reshape((3,4), order='C')
print(r3)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

随机抽样(numpy.random

1. 简单的随机数据

1.1 rand(d0, d1, ..., dn)

随机值

np.random.rand(3,2)

array([[ 0.14022471,  0.96360618],  #random
       [ 0.37601032,  0.25528411],  #random
       [ 0.49313049,  0.94909878]]) #random

1.2 randn(d0, d1, ..., dn)

返回一个样本,具有标准正态分布(期望为 0,方差为 1 的正态分布)。

这个函数的作用就是从标准正态分布中返回一个或多个样本值。如果没有参数,则返回一个值,如果有参数,则返回 (d0, d1, …, dn) 个值,这些值都是从标准正态分布中随机取样得到的。

d0, d1, …, dn 都应该是整数,是浮点数也没关系,系统会自动把浮点数的整数部分截取出来。

参数

  • d0, d1, …, dn:应该为正整数,表示维度。

返回值

  • Zndarray 或者 float

如果想要从非标准正态分布中产生随机样本,例如下面这个正态分布:

N(\mu, \sigma^2)

可以这样做:

\sigma * np.random.randn(...) + \mu

例子:

np.random.randn()
# -0.8405297****8702

2.5 * np.random.randn(2, 4) + 3
# array([[ 4.128****53,  1.764****44 ,  2.732****92,  2.90839231],
#        [0.174****86,  4.92026887,  1.574****66, -0.4305991 ]])

这个函数与 numpy.random.standard_normal 函数非常相似,但是调用方式不一样:

numpy.random.standard_normal(size=None)

size 参数就是一个整数或者一个整数的元组,表示维度。例子:

np.random.standard_normal(8000)
np.random.standard_normal(size=(3, 4, 2))

1.3 randint(low[, high, size])

返回随机的整数,位于半开区间 [low, high)

np.random.randint(2, size=10)
# array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0])

np.random.randint(1, size=10)
# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

Generate a 2 x 4 array of int between 0 and 4, inclusive:

np.random.randint(5, size=(2, 4))
#array([[4, 0, 2, 1],
#       [3, 2, 2, 0]])

1.4 random_integers(low[, high, size])

返回随机的整数,位于闭区间 [low, high]

To sample from N evenly spaced floating-point numbers between a and b, use:

a + (b - a) * (np.random.random_integers(N) - 1) / (N - 1.)

例子:

np.random.random_integers(5)
# 4

type(np.random.random_integers(5))
# <type 'int'>

np.random.random_integers(5, size=(3., 2.))
# array([[5, 4],
#        [3, 3],
#        [4, 5]])

Choose five random numbers from the set of five evenly-spaced numbers between 0 and 2.5

2.5 * (np.random.random_integers(5, size=(5,)) - 1) / 4.
# array([ 0.625,  1.25 ,  0.625,  0.625,  2.5  ])

Roll two six sided dice 1000 times and sum the results:

d1 = np.random.random_integers(1, 6, 1000)
d2 = np.random.random_integers(1, 6, 1000)
dsums = d1 + d2

Display results as a histogram:

import matplotlib.pyplot as plt
count, bins, ignored = plt.hist(dsums, 11, normed=True)
plt.show()

1.5 random_sample([size])

返回随机的浮点数,在半开区间 [0.0, 1.0)

To sample multiply the output of random_sample by (b-a) and add a:

(b - a) * random_sample() + a

例子:

np.random.random_sample()
# 0.47108547995356098

type(np.random.random_sample())
# <type 'float'>

np.random.random_sample((5,))
# array([0.30220482, 0.86820401, 0.1654503, 0.11659149, 0.54323428])

Three-by-two array of random numbers from [-5, 0)

5 * np.random.random_sample((3, 2)) - 5
# array([[-3.99149989, -0.52338984],
#        [-2.99091858, -0.79479508],
#        [-1.23204345, -1.75224494]])

1.6 random([size])

返回随机的浮点数,在半开区间 [0.0, 1.0)

1.7 ranf([size])

返回随机的浮点数,在半开区间 [0.0, 1.0)

1.8 sample([size])

返回随机的浮点数,在半开区间 [0.0, 1.0)

1.9 choice(a[, size, replace, p])

def choice(a, size=None, replace=True, p=None)
  • 表示从 a 中随机选取 size 个数
  • replace:代表的意思是抽样之后还放不放回去,如果是 False 的话,那么通一次挑选出来的数都不一样,如果是 True 的话, 有可能会出现重复的,因为前面的抽的放回去了。
  • p:表示每个元素被抽取的概率,如果没有指定,a 中所有元素被选取的概率是相等的。

例子:

import numpy as np

offset = np.random.choice(100, 1)
print(type(offset))
# <class 'numpy.ndarray'>
print(offset)
# [7]
print(offset[0])
# 7
# 这样做可以将ndarray

Generate a uniform random sample from np.arange(5) of size 3:

np.random.choice(5, 3)
# array([0, 3, 4])
# This is equivalent to np.random.randint(0,5,3)

Generate a non-uniform random sample from np.arange(5) of size 3:

np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0])
# array([3, 3, 0])

Generate a uniform random sample from np.arange(5) of size 3without replacement:

np.random.choice(5, 3, replace=False)
# array([3,1,0])
# This is equivalent to np.random.permutation(np.arange(5))[:3]

Generate a non-uniform random sample from np.arange(5) of size 3 without replacement:

np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0])
# array([2, 3, 0])

Any of the above can be repeated with an arbitrary array-like instead of just integers. For instance(可以对列表 list 类型元素使用):

aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher']
np.random.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3])
# array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'])

1.10 bytes(length)

返回随机字节。

np.random.bytes(10)
# ' eh\x85\x022SZ\xbf\xa4' #random

2. 排列

2.1 shuffle(x)

现场修改序列,改变自身内容。(类似洗牌,打乱顺序)

arr = np.arange(10)
np.random.shuffle(arr)
print(arr)
# [1 7 5 2 9 4 3 6 0 8]

This function only shuffles the array along the first index of a multi-dimensional array:

arr = np.arange(9).reshape((3, 3))
np.random.shuffle(arr)
print(arr)
# array([[3, 4, 5],
#        [6, 7, 8],
#        [0, 1, 2]])

2.2 permutation(x)

返回一个随机排列。

np.random.permutation(10)
# array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6])

np.random.permutation([1, 4, 9, 12, 15])
# array([15,  1,  9,  4, 12])

arr = np.arange(9).reshape((3, 3))
np.random.permutation(arr)
# array([[6, 7, 8],
#        [0, 1, 2],
#        [3, 4, 5]])

numpy 切片中的省略号 ...

它是对切片 [ : ] 里的 : 的拓展,但是它只能出现一个,就是说可以这样使用:[ : , : , : ],但 [ ... , ...] 就会报错。

常用的数据列表切片里的符号是冒号(形如 x[:5])。将 x[..., 1] 换为 x[:, 1] 并不会影响代码的结果。尝试下省略号是否可以用在所有列表数据类型呢?

a = [i+1 for i in range(100)]
a[..., 3]

# Traceback (most recent call last):
#   File "<input>", line 1, in <module>
# TypeError: list indices must be integers or slices, not tuple

看来 Python 自带的 list 类型并不能用省略号:

import numpy as np

b = np.array(a)
b[..., 1]
# array(2)

看来 numpy 库中的 array 类型数据是可以使用该省略号的。值得注意的是,该 b[..., 1] 返回的数据类型也是 array 类型。

numpy 库的说明文档:

Ellipsis expand to the number of : objects needed to make a selection tuple of the same length as x.ndim.
Only the first ellipsis is expanded, any others are interpreted as :.

例子:

x = np.array([[[1],[2],[3]], [[4],[5],[6]]])
x[..., 0]
# array([[1, 2, 3],
#        [4, 5, 6]])

当然,手册中的这个例子 x[..., 0] 并不能替换为 x[:, 0] ,而是需要替换为 x[:, :, 0]

x = np.array([[[1],[2],[3]], [[4],[5],[6]]])
x[..., 0]
# array([[1, 2, 3],
#        [4, 5, 6]])

x[:, 0]
# array([[1],
#        [4]])

x[:, :, 0]
# array([[1, 2, 3],
#        [4, 5, 6]])
import numpy as np

data = np.array([1, 2, 3, 4, 5, 6, 7 ,8])
order = np.random.permutation(len(data))
data = data[order, ...]
print(data)
# [3 2 8 1 6 5 4 7]

np.mean

numpy.mean(a, axis, dtype, out,keepdims)

主要用于求取均值。

经常操作的参数为 axis,以 m*n 矩阵举例:

  • axis 不设置值,对 m*n 个数求均值,返回一个实数

  • axis = 0:压缩行,对各列求均值,返回 1*n 矩阵

  • axis =1 :压缩列,对各行求均值,返回 m*1 矩阵

例子:

1. 数组的操作

a = np.array([[1, 2], [3, 4]])
# array([[1, 2],
#        [3, 4]])

np.mean(a)
# 2.5

np.mean(a, axis=0) # axis=0,计算每一列的均值
# array([ 2.,  3.])

np.mean(a, axis=1) # 计算每一行的均值 
# array([ 1.5,  3.5])

2. 矩阵的操作:

import numpy as np
num1 = np.array([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])
# array([[1, 2, 3],
#        [2, 3, 4],
#        [3, 4, 5],
#        [4, 5, 6]])

num2 = np.mat(num1)
# matrix([[1, 2, 3],
#         [2, 3, 4],
#         [3, 4, 5],
#         [4, 5, 6]])

np.mean(num2) # 对所有元素求均值
# 3.5

np.mean(num2, 0) # 压缩行,对各列求均值
# matrix([[ 2.5,  3.5,  4.5]])

np.mean(num2, 1) # 压缩列,对各行求均值
# matrix([[ 2.],
#         [ 3.],
#         [ 4.],
#         [ 5.]])

np.maxnp.maximum

1. 参数

首先比较二者的参数部分:

np.max(a, axis=None, out=None, keepdims=False) 
  • 求序列的最值
  • 最少接收一个参数
  • axis:默认为列向(也即 axis=0),axis = 1 时为行方向的最值
np.maximum(X, Y, out=None) 
  • XY 逐位比较取其大者
  • 最少接收两个参数

2. 使用

np.max([-2, -1, 0, 1, 2])
# 2

np.maximum([-2, -1, 0, 1, 2], 0)
# array([0, 0, 0, 1, 2])
# 当然 np.maximum 接受的两个参数,也可以大小一致
# 或者更为准确地说,第二个参数只是一个单独的值时,其实是用到了维度的 broadcast 机制

numpy.std()

计算矩阵标准差。

例子:

a = np.array([[1, 2], [3, 4]])
np.std(a) # 计算全局标准差
# 1.1180339887498949

np.std(a, axis=0) # axis=0计算每一列的标准差
# array([ 1.,  1.])

np.std(a, axis=1) # 计算每一行的标准差
# array([ 0.5,  0.5])

np.pad()

在卷积神经网络中,为了避免因为卷积运算导致输出图像缩小和图像边缘信息丢失,常常采用图像边缘填充技术,即在图像四周边缘填充 0,使得卷积运算后图像大小不会缩小,同时也不会丢失边缘和角落的信息。在 Python 的 numpy 库中,常常采用 numpy.pad() 进行填充操作。

1. 函数原型

pad(array, pad_width, mode, **kwargs)

返回值:数组

参数:

  • array — 表示需要填充的数组
  • pad_width — 表示每个轴(axis)边缘需要填充的数值数目。参数输入方式为:((before_1, after_1), … (before_N, after_N)),其中 (before_1, after_1) 表示第 1 轴两边缘分别填充 before_1 个和 after_1 个数值。
  • mode — 表示填充的方式(取值:str 字符串或用户提供的函数),总共有 11 种填充模式

填充方式:

  • 'constant' — 表示连续填充相同的值,每个轴可以分别指定填充值,constant_values = (x, y) 时前面用 x 填充,后面用 y 填充,缺省值填充 0
  • 'edge' — 表示用边缘值填充
  • 'linear_ramp' — 表示用边缘递减的方式填充
  • 'maximum' — 表示最大值填充
  • 'mean' — 表示均值填充
  • 'median' — 表示中位数填充
  • 'minimum' — 表示最小值填充
  • 'reflect' — 表示对称填充
  • 'symmetric' — 表示对称填充
  • 'wrap' — 表示用原数组后面的值填充前面,前面的值填充后面

对一维数组的填充:

import numpy as np
arr1D = np.array([1, 1, 2, 2, 3, 4])

'''不同的填充方法'''
print('constant: '       + str(np.pad(arr1D, (2, 3), 'constant'))
print('edge: '           + str(np.pad(arr1D, (2, 3), 'edge'))
print('linear_ramp: ' + str(np.pad(arr1D, (2, 3), 'linear_ramp'))
print('maximum: '       + str(np.pad(arr1D, (2, 3), 'maximum'))
print('mean: '           + str(np.pad(arr1D, (2, 3), 'mean'))
print('median: '       + str(np.pad(arr1D, (2, 3), 'median'))
print('minimum: '     + str(np.pad(arr1D, (2, 3), 'minimum'))
print('reflect: '     + str(np.pad(arr1D, (2, 3), 'reflect'))
print('symmetric: '   + str(np.pad(arr1D, (2, 3), 'symmetric'))
print('wrap: '        + str(np.pad(arr1D, (2, 3), 'wrap')) 
# (2,3)表示前面两个,后面三个

对多维数组的填充:

import numpy as np

arr3D = np.array([[[1, 1, 2, 2, 3, 4], [1, 1, 2, 2, 3, 4], [1, 1, 2, 2, 3, 4]], 
                  [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], 
                  [[1, 1, 2, 2, 3, 4], [1, 1, 2, 2, 3, 4], [1, 1, 2, 2, 3, 4]]])

'''对于多维数组'''
print('constant: \n'    + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'constant')))
print('edge:  \n'         + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'edge')))
print('linear_ramp: \n' + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'linear_ramp')))
print('maximum: \n'     + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'maximum')))
print('mean: \n'        + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'mean')))
print('median: \n'         + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'median')))
print('minimum: \n'     + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'minimum')))
print('reflect: \n'     + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'reflect')))
print('symmetric: \n'     + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'symmetric')))
print('wrap: \n'         + str(np.pad(arr3D, ((0, 0), (1, 1), (2, 2)), 'wrap')))

np.transpose

参数:

  • a:输入数组
  • axisint 类型的列表,这个参数是可选的。默认情况下,反转的输入数组的维度,当给定这个参数时,按照这个参数所定的值进行数组变换。

返回值:

  • pndarray 类型,返回转置过后的原数组的视图。

1. 一维数组

import numpy as np
t=np.arange(4)
# array([0, 1, 2, 3])

t.transpose()
# array([0, 1, 2, 3])
# 对于一维数组而言,numpy.transpose()是不起作用的

2. 二维数组

two=np.arange(16).reshape(4,4)
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [ 8,  9, 10, 11],
#        [12, 13, 14, 15]])

two.transpose()
# array([[ 0,  4,  8, 12],
#        [ 1,  5,  9, 13],
#        [ 2,  6, 10, 14],
#        [ 3,  7, 11, 15]])

two.transpose(1,0)
# array([[ 0,  4,  8, 12],
#        [ 1,  5,  9, 13],
#        [ 2,  6, 10, 14],
#        [ 3,  7, 11, 15]])
# 对于三维数组,原始axis排列为(0,1,2),numpy.transpose()默认的参数为(2,1,0)得到转置后的数组的视图,不影响原数组的内容以及大小。 

np.arange() 函数

返回值:

  • np.arange() 函数返回一个有终点和起点的固定步长的排列,如 [1,2,3,4,5],起点是 1,终点是 5,步长为 1

参数个数情况:np.arange() 函数分为一个参数,两个参数,三个参数三种情况:

  1. 一个参数时,参数值为终点,起点取默认值 0,步长取默认值 1
  2. 两个参数时,第一个参数为起点,第二个参数为终点,步长取默认值 1
  3. 三个参数时,第一个参数为起点,第二个参数为终点,第三个参数为步长(其中步长支持小数)。

例子:

#一个参数 默认起点0,步长为1 输出:[0 1 2]
a = np.arange(3)

#两个参数 默认步长为1 输出[3 4 5 6 7 8]
a = np.arange(3,9)

#三个参数 起点为0,终点为4,步长为0.1 输出[ 0\.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1\.   1.1  1.2  1.3  1.4 1.5  1.6  1.7  1.8  1.9  2\.   2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9]
a = np.arange(0, 3, 0.1)

例子:

B = 2
S = 7
x = np.array([np.arange(S)] * S * B)
# np.arange(S)创建一个一维数组:[0,1,2,3,4,5,6]
# 而S*B=14,就把一维数组复制14遍
# 相当于创建一个7*14的二维数组
print(x)

# [[0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]
#  [0 1 2 3 4 5 6]]

// 运算符

取整除 - 返回商的整数部分(向下取整)。

例子:

9//2
# 4

-9//2
# -5