python学习之旅_第2天

python学习之旅 专栏收录该内容
3 篇文章 0 订阅

学习目标:

三天掌握 python 入门知识


学习内容:

1、 Python3 迭代器与生成器
2、 Python3 函数
3、 Python3 数据结构
4、 Python3 模块
5、 Python3 输入和输出
6、 Python3 File(文件) 方法
7、 Python3 OS 文件/目录方法
8、 Python3 错误和异常
9、 Python3 面向对象
10、Python3 命名空间和作用域
11、Python3 标准库概览
12、Python3 实例


学习时间:

周六至周日上午 7 点—晚上9点


学习产出:

1、 技术笔记 1 遍
2、CSDN 技术博客 3 篇
3、 学习的 vlog 视频 3 个

学习笔记:

  • 1、 Python3 迭代器与生成器

    迭代器

     迭代是Python最强大的功能之一,是访问集合元素的一种方式。
    

    ​ 迭代器是一个可以记住遍历的位置的对象。

    ​ 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。

    ​ 迭代器只能往前不会后退。

    ​ 迭代器有两个基本的方法:iter() 和 next()。

    ​ 字符串,列表或元组对象都可用于创建迭代器:

    创建一个迭代器
    

    ​ 把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。

    	面向对象编程,类都有一个构造函数,Python 的构造函数为 __init__(), 它会在对象初始化的时候执行。
    

    ​ 更多内容查阅:Python3 面向对象

    __iter__() 方法返回一个特殊的迭代器对象,实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
    
    __next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。
    
    StopIteration
    StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
    

    生成器

    在 Python 中,使用了 yield 的函数被称为生成器(generator)。

    跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

    在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

    调用一个生成器函数,返回的是一个迭代器对象。

     实例使用 yield 实现斐波那契数列 :
     
    import sys
    def fibonacci(n): # 生成器函数 - 斐波那契
        a, b, counter = 0, 1, 0
        while True:
            if (counter > n): 
                return
            yield a
            a, b = b, a + b
            counter += 1
    f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
     
    while True:
        try:
            print (next(f), end=" ")
        except StopIteration:
            sys.exit()
    

    有yield和没有yield的情况会对生成器了解多点:

    第一种:使用 yield

    #!/usr/bin/python3
    
    import sys
    
    def fibonacci(n,w=0): # 生成器函数 - 斐波那契
        a, b, counter = 0, 1, 0
        while True:
            if (counter > n): 
                return
            yield a
            a, b = b, a + b
            print('%d,%d' % (a,b))
            counter += 1
    f = fibonacci(10,0) # f 是一个迭代器,由生成器返回生成
    
    while True:
        try:
            print (next(f), end=" ")
        except :
            sys.exit()
    

    输出结果:

    0 1,1
    1 1,2
    1 2,3
    2 3,5
    3 5,8
    5 8,13
    8 13,21
    13 21,34
    21 34,55
    34 55,89
    55 89,144
    

    第二种:不使用 yield

    #!/usr/bin/python3
    
    import sys
    
    def fibonacci(n,w=0): # 生成器函数 - 斐波那契
        a, b, counter = 0, 1, 0
        while True:
            if (counter > n): 
                return
            #yield a
            a, b = b, a + b
            print('%d,%d' % (a,b))
            counter += 1
    f = fibonacci(10,0) # f 是一个迭代器,由生成器返回生成
    
    while True:
        try:
            print (next(f), end=" ")
        except :
            sys.exit()
    

    输出结果:

    1,1
    1,2
    2,3
    3,5
    5,8
    8,13
    13,21
    21,34
    34,55
    55,89
    89,144
    

    第二种没有yield时,函数只是简单执行,没有返回迭代器f。这里的迭代器可以用生成l列表来理解一下:

    >>> l = [i for i in range(0,15)]
    >>> print(l)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    >>> m = (i for i in range(0,15))
    >>> print(m)
    <generator object <genexpr> at 0x104b6f258>
    >>> for g in m:
    ...     print(g,end=', ')
    ... 
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
    

    什么情况下需要使用 yield?

    一个函数 f,f 返回一个 list,这个 list 是动态计算出来的(不管是数学上的计算还是逻辑上的读取格式化),并且这个 list 会很大(无论是固定很大还是随着输入参数的增大而增大),这个时候,我们希望每次调用这个函数并使用迭代器进行循环的时候一个一个的得到每个 list 元素而不是直接得到一个完整的 list 来节省内存,这个时候 yield 就很有用。

    具体怎么使用 yield 参考:Python yield 使用浅析

    以斐波那契函数为例,我们一般希望从 n 返回一个 n 个数的 list:

    def fab(max): 
       n, a, b = 0, 0, 1 
       L = [] 
       while n < max: 
           L.append(b) 
           a, b = b, a + b 
           n = n + 1 
       return L
    

    上面那个 fab 函数从参数 max 返回一个有 max 个元素的 list,当这个 max 很大的时候,会非常的占用内存。

    一般我们使用的时候都是这个样子的,比如:

    f = iter(fab(1000))
    while True:
        try:
            print (next(f), end=" ")
        except StopIteration:
            sys.exit()
    

    这样我们实际上是先生成了一个 1000 个元素的 list:f,然后我们再去使用这个 f。

    现在,我们换一个方法:

    因为我们实际使用的是 list 的遍历,也就是 list 的迭代器。那么我们可以让这个函数 fab 每次只返回一个迭代器——一个计算结果,而不是一个完整的 list:

    def fab(max): 
        n, a, b = 0, 0, 1 
        while n < max: 
            yield b 
            # print b 
            a, b = b, a + b 
            n = n + 1 
    

    这样,我们每次调用fab函数,比如这样:

    for x in fab(1000):
        print(x)
    

    或者 next 函数之类的,实际上的运行方式是每次的调用都在 yield 处中断并返回一个结果,然后再次调用的时候再恢复中断继续运行。

def get():
    m = 0
    n = 2
    l = ['s',1,3]
    k = {1:1,2:2}
    p = ('2','s','t')
    while True:
        m += 1
        yield m
        yield m ,n ,l ,k ,p
        
it = get()
print(next(it)) #1
print(next(it)) #(1, 2, ['s', 1, 3], {1: 1, 2: 2}, ('2', 's', 't'))

print(next(it)) #2
print(type(next(it))) #<class 'tuple'>

如果再加一句:

print(type(next(it))) #<class 'int'>  #返回的是整形

所以返回值的类型,应该是当前调用时,yield 返回值的类型。

字符串,列表或元组对象都可用于创建迭代器。

字符串(Strings):

普通的旧字符串也是可迭代的。

for s in "hello":
    print s

输出结果为:

h
e
l
l
o

列表(Lists):

这些可能是最明显的迭代。

for x in [None,3,4.5,"foo",lambda : "moo",object,object()]:
    print "{0}  ({1})".format(x,type(x))

输出结果为:

None  (<type 'NoneType'>)
3  (<type 'int'>)
4.5  (<type 'float'>)
foo  (<type 'str'>)
<function <lambda> at 0x7feec7fa7578>  (<type 'function'>)
<type 'object'>  (<type 'type'>)
<object object at 0x7feec7fcc090>  (<type 'object'>)

元组(Tuples):

元组在某些基本方面与列表不同,注意到以下示例中的可迭代对象使用圆括号而不是方括号,但输出与上面列表示例的输出相同。

for x in (None,3,4.5,"foo",lambda : "moo",object,object()):
    print "{0}  ({1})".format(x,type(x))

输出结果为:

None  (<type 'NoneType'>)
3  (<type 'int'>)
4.5  (<type 'float'>)
foo  (<type 'str'>)
<function <lambda> at 0x7feec7fa7578>  (<type 'function'>)
<type 'object'>  (<type 'type'>)
<object object at 0x7feec7fcc090>  (<type 'object'>)

字典(Dictionaries):

字典是键值对的无序列表。当您使用for循环遍历字典时,您的虚拟变量将使用各种键填充。

d = {
  'apples' : 'tasty',
  'bananas' : 'the best',
  'brussel sprouts' : 'evil',
  'cauliflower' : 'pretty good'
}

for sKey in d:
  print "{0} are {1}".format(sKey,d[sKey])

输出结果为:

brussel sprouts are evil
apples are tasty
cauliflower are pretty good
bananas are the best

也许不是这个顺序,字典是无序的!!!

自定义迭代器实现斐波那契数列

class Fibonacci:
  def __init__(self, count):
    self.count = count

  def __iter__(self):
    self.i = 0
    self.a, self.b = 0, 1
    return self

  def __next__(self):
  if self.i < self.count:
    self.i += 1
    a_old = self.a
    self.a, self.b = self.b, self.a + self.b
    return a_old
  else:
    raise StopIteration

for i in Fibonacci(10):
  print(i, end=" ")

可迭代、迭代器、生成器三个概念的联系和区别。

1、可迭代概念范围最大,生成器和迭代器肯定都可迭代,但可迭代不一定都是迭代器和生成器,比如上面说到的内置集合类数据类型。可以认为,在 Python 中,只要有集合特性的,都可迭代。

2、迭代器,迭代器特点是,均可以使用 for in 和 next 逐一遍历。

3、生成器,生成器一定是迭代器,也一定可迭代。

至于 Python 中为何要引入迭代器和生成器,除了节省内存空间外,也可以显著提升代码运行速度。

自定义迭代器类示例和说明如下:

class MyIter():
  def __init__(self):
    #为了示例,用一个简单的列表作为需迭代的数据集合,并且私有化可视情况变为其他类型集合
    self.__list1=[1,2,3,4]
    self.__index=0

  def __iter__(self):
    #该魔法方法,必须返回一个迭代器对象,如果self已经定义了__next__()魔法方法,则只需要返回self即可
    #因为如上面所述,生成器一定是迭代器
    return iter(self.list1)    

  def __next__(self):
    #此处的魔法函数,python会自动记忆每次迭代的位置,无需再使用yield来处理
    #在使用next(obj)时,会自动调用该魔法方法
    res=self.__list1[self.__index]
    self.__index+=1
    return res

以上为自定义迭代器类的机制。

下面再示例说明下,如何自定义生成器函数,因为大多数实战场景中,使用生成器函数可能会更多一些:

def my_gene_func():
  index=0
  li=[1,2,3,4,5]
  yield li[index]
  index+=1

调用以上函数时,会返回一个生成器对象,然后对该生成器对象,使用 next() 逐一返回:

gene=my_gene_func()
next(gene)

其实核心的概念还是记忆上次迭代的位置,类中直接使用 next 魔法方法实现,函数中使用 yield 实现。且怀疑,类中的 next 魔法方法底层也是使用 yield 来实现的。

迭代器和生成器具体应用场景,就凡是需要提升运行效率或节约内存资源,且遍历的数据是集合形式的,都可以考虑。

另外一个小众的使用场景,是变相实现协程的效果,即在同一个线程内,实现不同任务交替执行

def mytask1():
  print('task1 开始执行')
  '''
  task code
  '''
  yield

def mytask2():
  print('task2 开始执行')
  '''
  task code
  '''
  yield

gene1=mytask1()
gene2=mytask2()

for i in range(100):
  next(gene1)
  next(gene2)
  • 2、 Python3 函数

    函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

    函数能提高应用的模块性,和代码的重复利用率。

    你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

    定义一个函数

    你可以定义一个由自己想要功能的函数,以下是简单的规则:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsmgngaZ-1618047520959)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1618040335533.png)]

    语法

    Python 定义函数使用 def 关键字,一般格式如下:

    def 函数名(参数列表):
        函数体
    

    默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。

    函数调用

    定义一个函数:给了函数一个名称,指定了函数里包含的参数,和代码块结构。

    这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从 Python 命令提示符执行。

    参数传递

    在 python 中,类型属于对象,变量是没有类型的:

    a=[1,2,3]
    
    a="Runoob"
    

    以上代码中,[1,2,3] 是 List 类型,“Runoob” 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。

    可更改(mutable)与不可更改(immutable)对象

    在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

    • **不可变类型:**变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。
    • **可变类型:**变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

    python 函数的参数传递:

    • **不可变类型:**类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
    • **可变类型:**类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响

    python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

    python 传不可变对象实例

    通过 id() 函数来查看内存地址变化:

    在调用函数前后,形参和实参指向的是同一个对象(对象 id 相同),在函数内部修改形参后,形参指向的是不同的 id。

    传可变对象实例

    可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了。

    参数

    以下是调用函数时可使用的正式参数类型:

    • 必需参数
    • 关键字参数
    • 默认参数
    • 不定长参数

    必需参数

    必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

    调用 printme() 函数,你必须传入一个参数,不然会出现语法错误:

    关键字参数

    关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。

    使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

    默认参数

    调用函数时,如果没有传递参数,则会使用默认参数。

    不定长参数

    可能需要一个函数能处理比当初声明时更多的参数。

    这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名。

     加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
     如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。
    
    还有一种就是参数带两个星号 **基本语法如下:
    def functionname([formal_args,] **var_args_dict ):
       "函数_文档字符串"
       function_suite
       return [expression]
    

    声明函数时,参数中星号 ***** 可以单独出现 , 如果单独出现星号 ***** 后的参数必须用关键字传入。

    匿名函数

    python 使用 lambda 来创建匿名函数。

    所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

    • lambda 只是一个表达式,函数体比 def 简单很多。
    • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
    • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
    • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

    语法

    lambda 函数的语法只包含一个语句,如下:

    lambda [arg1 [,arg2,.....argn]]:expression
    

    return语句

    return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None

    强制位置参数

    Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。

  • 3、 Python3 数据结构

    列表

    Python中列表是可变的,这是它区别于字符串和元组的最重要的特点,一句话概括即:列表可以修改,而字符串和元组不能。

    以下是 Python 中列表的方法:

    方法描述
    list.append(x)把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。
    list.extend(L)通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。
    list.insert(i, x)在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。
    list.remove(x)删除列表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。
    list.pop([i])从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
    list.clear()移除列表中的所有项,等于del a[:]。
    list.index(x)返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
    list.count(x)返回 x 在列表中出现的次数。
    list.sort()对列表中的元素进行排序。
    list.reverse()倒排列表中的元素。
    list.copy()返回列表的浅复制,等于a[:]。

    将列表当做堆栈使用

    列表方法使得列表可以很方便的作为一个堆栈来使用,堆栈作为特定的数据结构,最先进入的元素最后一个被释放(后进先出)。用 append() 方法可以把一个元素添加到堆栈顶。用不指定索引的 pop() 方法可以把一个元素从堆栈顶释放出来。

    将列表当作队列使用

    也可以把列表当做队列用,只是在队列里第一加入的元素,第一个取出来;但是拿列表用作这样的目的效率不高。在列表的最后添加或者弹出元素速度快,然而在列表里插入或者从头部弹出速度却不快(因为所有其他的元素都得一个一个地移动)。

    列表推导式

    列表推导式提供了从序列创建列表的简单途径。通常应用程序将一些操作应用于某个序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的判定条件创建子序列。

    每个列表推导式都在 for 之后跟一个表达式,然后有零到多个 for 或 if 子句。返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表。如果希望表达式推导出一个元组,就必须使用括号。

    嵌套列表解析

    Python的列表还可以嵌套。

    del 语句

    使用 del 语句可以从一个列表中依索引而不是值来删除一个元素。

    这与使用 pop() 返回一个值不同。

    可以用 del 语句从列表中删除一个切割,或清空整个列表(我们以前介绍的方法是给该切割赋一个空列表)。

    遍历技巧

    在字典中遍历时,关键字和对应的值可以使用 items() 方法同时解读出来:

    \>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
    \>>> **for** k, v **in** knights.items():
    ...   **print**(k, v)
    ...
    gallahad the pure
    robin the brave
    

    在序列中遍历时,索引位置和对应值可以使用 enumerate() 函数同时得到:

    >>> **for** i, v **in** enumerate(['tic', 'tac', 'toe']):
    ...   **print**(i, v)
    ...
    0 tic
    1 tac
    2 toe
    
    同时遍历两个或更多的序列,可以使用 zip() 组合:
    
    \>>> questions = ['name', 'quest', 'favorite color']
    \>>> answers = ['lancelot', 'the holy grail', 'blue']
    \>>> **for** q, a **in** zip(questions, answers):
    ...   **print**('What is your {0}?  It is {1}.'.format(q, a))
    ...
    What **is** your name?  It **is** lancelot.
    What **is** your quest?  It **is** the holy grail.
    What **is** your favorite color?  It **is** blue.
    

    要反向遍历一个序列,首先指定这个序列,然后调用 reversed() 函数:

    \>>> **for** i **in** reversed(range(1, 10, 2)):
    ...   **print**(i)
    ...
    9
    7
    5
    3
    1
    

    要按顺序遍历一个序列,使用 sorted() 函数返回一个已排序的序列,并不修改原值:

    \>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
    \>>> **for** f **in** sorted(set(basket)):
    ...   **print**(f)
    ...
    apple
    banana
    orange
    pear
    

    遍历 dict 使用的 .items() 方法配合 for 循环,非常简明易懂,但有一项需要注意的是,在 for 循环中,使用单个变量和双变量的区别,注意观察以下两个例子的区别:

    >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
    >>> for k, v in knights.items():
    ...     print(k, v)
    ...
    gallahad the pure
    robin the brave
    
    >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
    >>> for k in knights.items():
    ...     print(k)
    ...
    ('gallahad', 'the pure')
    ('robin', 'the brave')
    

    使用 k 和 v 两个变量时,将键与值分别赋予 k 和 v。使用 k 一个变量时,将对应的键与值作为一个整体赋给 k。所以,最终 print 的显示内容是有区别的。不只是此例,程序设计过程中有很多地方都会体现个体与整体的差异,虽然显示出来的结果非常相似,但逻辑上却是完全不同的。

    使用小括号包裹推导式会生成生成器对象,而不是元组。

    >>> a = (2*x for x in range(2))
    >>> type(a)
    <class 'generator'>
    >>> next(a)
    0
    >>> next(a)
    2
    >>> next(a)
    Traceback (most recent call last):
      File "<pyshell#691>", line 1, in <module>
        next(a)
    StopIteration
    

    参阅文档

  • 4、 Python3 模块

    模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。

    ​ 1、import sys 引入 python 标准库中的 sys.py 模块;这是引入某一模块的方法。

    ​ 2、sys.argv 是一个包含命令行参数的列表。

    ​ 3、sys.path 包含了一个 Python 解释器自动查找所需模块的路径的列表

    import 语句

    想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:

    import module1[, module2[,... moduleN]
    

    当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

    搜索路径是一个解释器会先进行搜索的所有目录的列表。

    一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。

    这就涉及到Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。

    这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。

    搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量

    sys.path 输出是一个列表,其中第一项是空串’’,代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),亦即我们执行python解释器的目录(对于脚本的话就是运行的脚本所在的目录)。

    因此若像我一样在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。

    了解了搜索路径的概念,就可以在脚本中修改sys.path来引入一些不在搜索路径中的模块。

    深入模块

    模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。

    每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。

    所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞混。

    从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。

    模块是可以导入其他模块的。在一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,当然这只是一个惯例,而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。

    还有一种导入的方法,可以使用 import 直接把模块内(函数,变量的)名称导入到当前操作模块。

    __name__属性

    一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

    说明: 每个模块都有一个__name__属性,当其值是’main’时,表明该模块自身在运行,否则是被引入。

    说明:namemain 底下是双下划线, _ _ 是这样去掉中间的那个空格。

    dir() 函数

    内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回:

    标准模块

    Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。

    有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。

    这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。

    应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。

    包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。

    比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

    就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

    这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。

    不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。

    现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。

    并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。

    在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

    目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

    最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。

    用户可以每次只导入一个包里面的特定模块,比如:

    import sound.effects.echo
    

    这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:

    sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
    

    还有一种导入子模块的方法是:

    from sound.effects import echo
    

    这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:

    echo.echofilter(input, output, delay=0.7, atten=4)
    

    还有一种变化就是直接导入一个函数或者变量:

    from sound.effects.echo import echofilter
    

    同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:

    echofilter(input, output, delay=0.7, atten=4)
    

    注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

    import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。

    反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。


    从一个包中导入*

    如果我们使用 from sound.effects import * 会发生什么呢?

    Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。

    但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。

    在 Windows 平台平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。

    为了解决这个问题,我们只需要提供一个精确包的索引。

    导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

    作为包的作者,可别忘了在更新包之后保证 all 也更新了啊。

    以下实例在 file:sounds/effects/init.py 中包含如下代码:

    __all__ = ["echo", "surround", "reverse"]
    

    这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。

    如果 all 真的没有定义,那么使用**from sound.effects import ***这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。

    这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:

    import sound.effects.echo
    import sound.effects.surround
    from sound.effects import *
    

    这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)

    通常我们并不主张使用 ***** 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。

    记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。

    如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。

    from . import echo
    from .. import formats
    from ..filters import equalizer
    

    无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"main",一个Python应用程序的主模块,应当总是使用绝对路径引用。

    包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。

    这个功能并不常用,一般用来扩展包里面的模块。

  • 5、Python3 输入和输出

    输出格式美化

    Python两种输出值的方式: 表达式语句和 print() 函数。

    第三种方式是使用文件对象的 write() 方法,标准输出文件可以用 sys.stdout 引用。

    如果你希望输出的形式更加多样,可以使用 str.format() 函数来格式化输出值。

    如果你希望将输出的值转成字符串,可以使用 repr() 或 str() 函数来实现。

    str(): 函数返回一个用户易读的表达形式。
    repr(): 产生一个解释器易读的表达形式。
    

    字符串对象的 rjust() 方法, 它可以将字符串靠右, 并在左边填充空格。

    还有类似的方法, 如 ljust() 和 center()。 这些方法并不会写任何东西, 它们仅仅返回新的字符串。

    另一个方法 zfill(), 它会在数字的左边填充 0,

    str.format() 的基本使用如下:

    >>> **print**('{}网址: "{}!"'.format('菜鸟教程', 'www.runoob.com'))
    菜鸟教程网址: "www.runoob.com!"
    

    括号及其里面的字符 (称作格式化字段) 将会被 format() 中的参数替换。

    如果在 format() 中使用了关键字参数, 那么它们的值会指向使用该名字的参数。

    >>> print(’{name}网址: {site}’.format(name=‘菜鸟教程’, site=‘www.runoob.com’))
    菜鸟教程网址: www.runoob.com

    位置及关键字参数可以任意的结合:

    >>> print(‘站点列表 {0}, {1}, 和 {other}。’.format(‘Google’, ‘Runoob’, other=‘Taobao’))
    站点列表 Google, Runoob, 和 Taobao。

    !a (使用 ascii()), !s (使用 str()) 和 !r (使用 repr()) 可以用于在格式化某个值之前对其进行转化:

    >>> import math
    >>> print(‘常量 PI 的值近似为: {}。’.format(math.pi))
    常量 PI 的值近似为: 3.141592653589793。
    >>> print(‘常量 PI 的值近似为: {!r}。’.format(math.pi))
    常量 PI 的值近似为: 3.141592653589793。

    可选项 : 和格式标识符可以跟着字段名。 这就允许对值进行更好的格式化。 下面的例子将 Pi 保留到小数点后三位:

    >>> import math
    >>> print(‘常量 PI 的值近似为 {0:.3f}。’.format(math.pi))
    常量 PI 的值近似为 3.142。

    : 后传入一个整数, 可以保证该域至少有这么多的宽度。 用于美化表格时很有用。

    >>> table = {‘Google’: 1, ‘Runoob’: 2, ‘Taobao’: 3}
    >>> for name, number in table.items():
    print(’{0:10} ==> {1:10d}’.format(name, number))

    Google ==> 1
    Runoob ==> 2
    Taobao ==> 3

    如果你有一个很长的格式化字符串, 而你不想将它们分开, 那么在格式化时通过变量名而非位置会是很好的事情。

    最简单的就是传入一个字典, 然后使用方括号 [] 来访问键值 :

    >>> table = {‘Google’: 1, ‘Runoob’: 2, ‘Taobao’: 3}
    >>> print(‘Runoob: {0[Runoob]:d}; Google: {0[Google]:d}; Taobao: {0[Taobao]:d}’.format(table))
    Runoob: 2; Google: 1; Taobao: 3

    也可以通过在 table 变量前使用 ** 来实现相同的功能:

    >>> table = {‘Google’: 1, ‘Runoob’: 2, ‘Taobao’: 3}
    >>> print(‘Runoob: {Runoob:d}; Google: {Google:d}; Taobao: {Taobao:d}’.format(**table))
    Runoob: 2; Google: 1; Taobao: 3


    旧式字符串格式化

    % 操作符也可以实现字符串格式化。 它将左边的参数作为类似 sprintf() 式的格式化字符串, 而将右边的代入, 然后返回格式化后的字符串. 例如:

    >>> import math
    >>> print(‘常量 PI 的值近似为:%5.3f。’ % math.pi)
    常量 PI 的值近似为:3.142。

    因为 str.format() 是比较新的函数, 大多数的 Python 代码仍然使用 % 操作符。但是因为这种旧式的格式化最终会从该语言中移除, 应该更多的使用 str.format().


    读取键盘输入

    Python 提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘。

    实例

    #!/usr/bin/python3

    str = input(“请输入:”);
    print ("你输入的内容是: ", str)

    这会产生如下的对应着输入的结果:

    请输入:菜鸟教程
    你输入的内容是:  菜鸟教程
    

    读和写文件

    open() 将会返回一个 file 对象,基本语法格式如下:

    open(filename, mode)
    
    • filename:包含了你要访问的文件名称的字符串值。
    • mode:决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读®。

    不同模式打开文件的完全列表:

    模式描述
    r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
    rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
    r+打开一个文件用于读写。文件指针将会放在文件的开头。
    rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
    w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
    模式rr+ww+aa+
    ++++
    +++++
    创建++++
    覆盖++
    指针在开始++++
    指针在结尾++

    第一个参数为要打开的文件名。

    第二个参数描述文件如何使用的字符。 mode 可以是 ‘r’ 如果文件只读, ‘w’ 只用于写 (如果存在同名文件则将被删除), 和 ‘a’ 用于追加文件内容; 所写的任何数据都会被自动增加到末尾. ‘r+’ 同时用于读写。 mode 参数是可选的; ‘r’ 将是默认值。

    文件对象的方法

    本节中剩下的例子假设已经创建了一个称为 f 的文件对象。

    f.read()

    为了读取一个文件的内容,调用 f.read(size), 这将读取一定数目的数据, 然后作为字符串或字节对象返回。

    size 是一个可选的数字类型的参数。 当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。

    f.readlines()

    f.readlines() 将返回该文件中包含的所有行。

    如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。

    f.write()

    f.write(string) 将 string 写入到文件中, 然后返回写入的字符数。

    f.tell()

    f.tell() 返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。

    f.seek()

    如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数。

    from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,例如:

    seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符

    seek(x,1) : 表示从当前位置往后移动x个字符

    seek(-x,2):表示从文件的结尾往前移动x个字符

    f.close()

    在文本文件中 (那些打开文件的模式下没有 b 的), 只会相对于文件起始位置进行定位。

    当你处理完一个文件后, 调用 f.close() 来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常。 当处理一个文件对象时, 使用 with 关键字是非常好的方式。在结束后, 它会帮你正确的关闭文件。 而且写起来也比 try - finally 语句块要简短:

    文件对象还有其他方法, 如 isatty() 和 trucate(), 但这些通常比较少用。

    pickle 模块

    python的pickle模块实现了基本的数据序列和反序列化。

    通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。

    通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。

    基本接口:

    pickle.dump(obj, file, [,protocol])
    

    有了 pickle 这个对象, 就能对 file 以读取的形式打开:

    x = pickle.load(file)
    

    **注解:**从 file 中读取一个字符串,并将它重构为原来的python对象。

    file: 类文件对象,有read()和readline()接口。

    通过 pickle 序列化实现一个简单联系人信息管理: 
    import pickle
    import os
    
    datafile = 'person.data'
    line = '======================================='
    message = '''
    =======================================
    Welcome bookmark:
        press 1 to show list
        press 2 to add pepole
        press 3 to edit pepole
        press 4 to delete pepole
        press 5 to search pepole
        press 6 to show menu
        press 0 to quit
    =======================================
    '''
    print(message)
    
    class Person(object):
        """通讯录联系人"""
        def __init__(self, name, number):
            self.name = name
            self.number = number
    
    # 获取数据
    def get_data(filename=datafile):
        # 文件存在且不为空
        if os.path.exists(filename) and os.path.getsize(filename):
            with open(filename,'rb') as f:
                return pickle.load(f)
        return None
            
    # 写入数据
    def set_data(name, number, filename=datafile):
    
        personList = {} if get_data() == None else get_data()
    
        with open(filename,'wb') as f:
            personList[name] = Person(name,number)
            pickle.dump(personList,f)
    
    # 保存字典格式的数据到文件
    def save_data(dictPerson, filename=datafile):
        with open(filename,'wb') as f:
            pickle.dump(dictPerson,f)
    
    # 显示所有联系人
    def show_all():
        personList = get_data()
        if personList:
            for v in personList.values():
                print(v.name,v.number)
            print(line)
        else:
            print('not yet person,please add person')
            print(line)
    
    # 添加联系人
    def add_person(name,number):
        set_data(name,number)
        print('success add person')
        print(line)
    
    # 编辑联系人
    def edit_person(name,number):
        personList = get_data()
        if personList:
            personList[name] = Person(name,number)
            save_data(personList)
            print('success edit person')
            print(line)
    
    # 删除联系人
    def delete_person(name):
        personList = get_data()
        if personList:
            if name in personList:
                del personList[name]
                save_data(personList)
                print('success delete person')
            else:
                print(name,' is not exists in dict')
            print(line)
    
    
    # 搜索联系人
    def search_person(name):
        personList = get_data()
        if personList:
            if name in personList.keys():
                print(personList.get(name).name, personList.get(name).number)
            else:
                print('No this person of ',name)
            print(line)
            
    
    while True:
        num = input('>>')
    
        if num == '1':
            print('show all personList:')
            show_all()
        elif num == '2':
            print('add person:')    
            name = input('input name>>')
            number = input('input number>>')
            add_person(name,number)
        elif num == '3':
            print('edit person:')
            name = input('input name>>')
            number = input('input number>>')
            edit_person(name,number)
        elif num == '4':
            print('delete person:')
            name = input('input name>>')
            delete_person(name)
        elif num == '5':
            print('search :')
            name = input('input name>>')
            search_person(name)
        elif num == '6':
            print(message)
        elif num == '0':
            break
        else:
            print('input error, please retry')
    
  • 6、 Python3 File(文件) 方法

    open() 方法

    Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。

    **注意:**使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。

    open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。

    open(file, mode='r')
    

    完整的语法格式为:

    open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
    

    参数说明:

    • file: 必需,文件路径(相对或者绝对路径)。
    • mode: 可选,文件打开模式
    • buffering: 设置缓冲
    • encoding: 一般使用utf8
    • errors: 报错级别
    • newline: 区分换行符
    • closefd: 传入的file参数类型
    • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

    mode 参数有:

    模式描述
    t文本模式 (默认)。
    x写模式,新建一个文件,如果该文件已存在则会报错。
    b二进制模式。
    +打开一个文件进行更新(可读可写)。
    U通用换行模式(Python 3 不支持)。
    r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
    rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
    r+打开一个文件用于读写。文件指针将会放在文件的开头。
    rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
    w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
    wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
    a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

    默认为文本模式,如果要以二进制模式打开,加上 b

    file 对象

    file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:

    序号方法及描述
    1file.close()关闭文件。关闭后文件不能再进行读写操作。
    2file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
    3file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
    4file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
    5file.next()**Python 3 中的 File 对象不支持 next() 方法。**返回文件下一行。
    6[file.read(size])从文件读取指定的字节数,如果未给定或为负则读取所有。
    7[file.readline(size])读取整行,包括 “\n” 字符。
    8[file.readlines(sizeint])读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
    9[file.seek(offset, whence])移动文件读取指针到指定位置
    10file.tell()返回文件当前位置。
    11[file.truncate(size])从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
    12file.write(str)将字符串写入文件,返回的是写入的字符长度。
    13file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。
    用户输入"xxx.txt"类文档文件名
    
    用户输入被替换的"待替换字"
    
    用户输入替换目标"新的字"
    
    用户判断是否全部替换 yes/no
    
    def file_replace(file_name, rep_word, new_word):
        f_read = open(file_name)
    
        content = []
        count = 0
    
        for eachline in f_read:
            if rep_word in eachline:
                count = count+eachline.count(rep_word)
                eachline = eachline.replace(rep_word, new_word)
            content.append(eachline)    
    
        decide = input('\n文件 %s 中共有%s个【%s】\n您确定要把所有的【%s】替换为【%s】吗?\n【YES/NO】:' \
                       % (file_name, count, rep_word, rep_word, new_word))
    
        if decide in ['YES', 'Yes', 'yes']:
            f_write = open(file_name, 'w')
            f_write.writelines(content)
            f_write.close()
    
        f_read.close()
    
    
    file_name = input('请输入文件名:')
    rep_word = input('请输入需要替换的单词或字符:')
    new_word = input('请输入新的单词或字符:')
    file_replace(file_name, rep_word, new_word)
    
  • 7、 Python3 OS 文件/目录方法

    Python3 OS 文件/目录方法

    os 模块提供了非常丰富的方法用来处理文件和目录。常用的方法如下表所示:

    序号方法及描述
    1os.access(path, mode) 检验权限模式
    2os.chdir(path) 改变当前工作目录
    3os.chflags(path, flags) 设置路径的标记为数字标记。
    4os.chmod(path, mode) 更改权限
    5os.chown(path, uid, gid) 更改文件所有者
    6os.chroot(path) 改变当前进程的根目录
    7os.close(fd) 关闭文件描述符 fd
    8os.closerange(fd_low, fd_high) 关闭所有文件描述符,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略
    9os.dup(fd) 复制文件描述符 fd
    10os.dup2(fd, fd2) 将一个文件描述符 fd 复制到另一个 fd2
    11os.fchdir(fd) 通过文件描述符改变当前工作目录
    12os.fchmod(fd, mode) 改变一个文件的访问权限,该文件由参数fd指定,参数mode是Unix下的文件访问权限。
    13os.fchown(fd, uid, gid) 修改一个文件的所有权,这个函数修改一个文件的用户ID和用户组ID,该文件由文件描述符fd指定。
    14os.fdatasync(fd) 强制将文件写入磁盘,该文件由文件描述符fd指定,但是不强制更新文件的状态信息。
    15[os.fdopen(fd, mode[, bufsize]]) 通过文件描述符 fd 创建一个文件对象,并返回这个文件对象
    16os.fpathconf(fd, name) 返回一个打开的文件的系统配置信息。name为检索的系统配置的值,它也许是一个定义系统值的字符串,这些名字在很多标准中指定(POSIX.1, Unix 95, Unix 98, 和其它)。
    17os.fstat(fd) 返回文件描述符fd的状态,像stat()。
    18os.fstatvfs(fd) 返回包含文件描述符fd的文件的文件系统的信息,Python 3.3 相等于 statvfs()。
    19os.fsync(fd) 强制将文件描述符为fd的文件写入硬盘。
    20os.ftruncate(fd, length) 裁剪文件描述符fd对应的文件, 所以它最大不能超过文件大小。
    21os.getcwd() 返回当前工作目录
    22os.getcwdb() 返回一个当前工作目录的Unicode对象
    23os.isatty(fd) 如果文件描述符fd是打开的,同时与tty(-like)设备相连,则返回true, 否则False。
    24os.lchflags(path, flags) 设置路径的标记为数字标记,类似 chflags(),但是没有软链接
    25os.lchmod(path, mode) 修改连接文件权限
    26os.lchown(path, uid, gid) 更改文件所有者,类似 chown,但是不追踪链接。
    27os.link(src, dst) 创建硬链接,名为参数 dst,指向参数 src
    28os.listdir(path) 返回path指定的文件夹包含的文件或文件夹的名字的列表。
    29os.lseek(fd, pos, how) 设置文件描述符 fd当前位置为pos, how方式修改: SEEK_SET 或者 0 设置从文件开始的计算的pos; SEEK_CUR或者 1 则从当前位置计算; os.SEEK_END或者2则从文件尾部开始. 在unix,Windows中有效
    30os.lstat(path) 像stat(),但是没有软链接
    31os.major(device) 从原始的设备号中提取设备major号码 (使用stat中的st_dev或者st_rdev field)。
    32os.makedev(major, minor) 以major和minor设备号组成一个原始设备号
    33[os.makedirs(path, mode]) 递归文件夹创建函数。像mkdir(), 但创建的所有intermediate-level文件夹需要包含子文件夹。
    34os.minor(device) 从原始的设备号中提取设备minor号码 (使用stat中的st_dev或者st_rdev field )。
    35[os.mkdir(path, mode]) 以数字mode的mode创建一个名为path的文件夹.默认的 mode 是 0777 (八进制)。
    36[os.mkfifo(path, mode]) 创建命名管道,mode 为数字,默认为 0666 (八进制)
    37[os.mknod(filename, mode=0600, device]) 创建一个名为filename文件系统节点(文件,设备特别文件或者命名pipe)。
    38[os.open(file, flags, mode]) 打开一个文件,并且设置需要的打开选项,mode参数是可选的
    39os.openpty() 打开一个新的伪终端对。返回 pty 和 tty的文件描述符。
    40os.pathconf(path, name) 返回相关文件的系统配置信息。
    41os.pipe() 创建一个管道. 返回一对文件描述符(r, w) 分别为读和写
    42[os.popen(command, mode[, bufsize]]) 从一个 command 打开一个管道
    43os.read(fd, n) 从文件描述符 fd 中读取最多 n 个字节,返回包含读取字节的字符串,文件描述符 fd对应文件已达到结尾, 返回一个空字符串。
    44os.readlink(path) 返回软链接所指向的文件
    45os.remove(path) 删除路径为path的文件。如果path 是一个文件夹,将抛出OSError; 查看下面的rmdir()删除一个 directory。
    46os.removedirs(path) 递归删除目录。
    47os.rename(src, dst) 重命名文件或目录,从 src 到 dst
    48os.renames(old, new) 递归地对目录进行更名,也可以对文件进行更名。
    49os.rmdir(path) 删除path指定的空目录,如果目录非空,则抛出一个OSError异常。
    50os.stat(path) 获取path指定的路径的信息,功能等同于C API中的stat()系统调用。
    51[os.stat_float_times(newvalue]) 决定stat_result是否以float对象显示时间戳
    52os.statvfs(path) 获取指定路径的文件系统统计信息
    53os.symlink(src, dst) 创建一个软链接
    54os.tcgetpgrp(fd) 返回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组
    55os.tcsetpgrp(fd, pg) 设置与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组为pg。
    56os.tempnam([dir[, prefix]]) **Python3 中已删除。**返回唯一的路径名用于创建临时文件。
    57os.tmpfile() **Python3 中已删除。**返回一个打开的模式为(w+b)的文件对象 .这文件对象没有文件夹入口,没有文件描述符,将会自动删除。
    58os.tmpnam() **Python3 中已删除。**为创建一个临时文件返回一个唯一的路径
    59os.ttyname(fd) 返回一个字符串,它表示与文件描述符fd 关联的终端设备。如果fd 没有与终端设备关联,则引发一个异常。
    60os.unlink(path) 删除文件路径
    61os.utime(path, times) 返回指定的path文件的访问和修改的时间。
    62[os.walk(top, topdown=True[, οnerrοr=None[, followlinks=False]]]) 输出在文件夹中的文件名通过在树中游走,向上或者向下。
    63os.write(fd, str) 写入字符串到文件描述符 fd中. 返回实际写入的字符串长度
    64os.path 模块 获取文件的属性信息。
    65os.pardir() 获取当前目录的父目录,以字符串形式显示目录名。
  • 8、 Python3错误和异常

    Python 有两种错误很容易辨认:语法错误和异常。

    Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

    语法错误

    Python 的语法错误或者称之为解析错

    SyntaxError: invalid syntax
    

    异常

    即便 Python 程序的语法是正确的,在运行它的时候,也有可能发生错误。运行期检测到的错误被称为异常。

    大多数的异常都不会被程序处理,都以错误信息的形式展现在这里:

    异常以不同的类型出现,这些类型都作为信息的一部分打印出来: 例子中的类型有 ZeroDivisionError,NameError 和 TypeError。
    错误信息的前面部分显示了异常发生的上下文,并以调用栈的形式显示具体信息。
    

    异常处理

    try/except

    异常捕捉可以使用 try/except 语句。

    img

    以下例子中,让用户输入一个合法的整数,但是允许用户中断这个程序(使用 Control-C 或者操作系统提供的方法)。

    try 语句按照如下方式工作;

    • 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)。
    • 如果没有异常发生,忽略 except 子句,try 子句执行后结束。
    • 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。
    • 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。

    一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。

    处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。

    一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如:

    except (RuntimeError, TypeError, NameError):
    pass

    最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。

    try/except…else

    try/except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。

    else 子句将在 try 子句没有发生任何异常的时候执行。

    img

    使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。

    异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常

    try-finally 语句

    try-finally 语句无论是否发生异常都将执行最后的代码。

    img

    抛出异常

    Python 使用 raise 语句抛出一个指定的异常。

    raise语法格式如下:

    raise [Exception [, args [, traceback]]]
    

    img

    raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

    with 是个好东西,打开文件的时候多使用它,可以避免很多问题。例如:

    temp = os.open('test_text.txt', os.O_RDWR | os.O_CREAT)
    temp_file = os.fdopen(temp, 'r')
    print(str(temp_file.read()))
    os.close(temp)
    

    就可以简化成:

    with open('test_text.txt', 'r') as f:
        print(f.read())
    

    预定义的清理行为

    一些对象定义了标准的清理行为,无论系统是否成功的使用了它,一旦不需要它了,那么这个标准的清理行为就会执行。

    相关内容

    Python assert(断言)

  • 9、 Python3 面向对象

    面向对象技术简介

    • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
    • **方法:**类中定义的函数。
    • **类变量:**类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
    • **数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
    • **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
    • **局部变量:**定义在方法中的变量,只作用于当前实例的类。
    • **实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
    • **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
    • **实例化:**创建一个类的实例,类的具体对象。
    • **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

    和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。

    Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。

    对象可以包含任意数量和类型的数据。

    类定义

    语法格式如下:

    class ClassName: . . .

    类对象

    类对象支持两种操作:属性引用和实例化。

    属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name

    类对象创建后,类命名空间中所有的命名都是有效属性名。

    类有一个名为 init() 的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样:

    def init(self): self.data = []

    类定义了 init() 方法,类的实例化操作会自动调用 init() 方法。如下实例化类 MyClass,对应的 init() 方法就会被调用:

    x = MyClass()
    

    当然, init() 方法可以有参数,参数通过 init() 传递到类的实例化操作上。

    self代表类的实例,而非类

    类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

    self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。

    self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:

    类的方法

    在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。

    继承

    Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义。

    子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法。

    BaseClassName(实例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用:

    class DerivedClassName(modname.BaseClassName):
    

    多继承

    Python同样有限的支持多继承形式。多继承的类定义形如下例:

    class DerivedClassName(Base1, Base2, Base3): . . .

    需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

    方法重写

    如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法。

    super() 函数是用于调用父类(超类)的一个方法。

    更多文档:

    Python 子类继承父类构造函数说明


    类属性与方法

    类的私有属性

    __private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

    类的方法

    在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。

    self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定是用 self

    类的私有方法

    __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods

    类的专有方法:

    • - **__init__ :** 构造函数,在生成对象时调用
      - **__del__ :** 析构函数,释放对象时使用
      - **__repr__ :** 打印,转换
      - **__setitem__ :** 按照索引赋值
      - **__getitem__:** 按照索引获取值
      - **__len__:** 获得长度
      - **__cmp__:** 比较运算
      - **__call__:** 函数调用
      - **__add__:** 加运算
      - **__sub__:** 减运算
      - **__mul__:** 乘运算
      - **__truediv__:** 除运算
      - **__mod__:** 求余运算
      - **__pow__:** 乘方
      

      运算符重载

      Python同样支持运算符重载,我们可以对类的专有方法进行重载

      复合重载运算符:

      • iadd: 加运算
      • isub: 减运算
      • imul: 乘运算
      • idiv: 除运算
      • imod: 求余运算
      • ipow: 乘方

      运算符重载的时候:

      #!/usr/bin/python3
      
      class Vector:
          def __init__(self, a, b):
              self.a = a
              self.b = b
          def __str__(self):
              return 'Vector (%d, %d)' % (self.a, self.b)
      
          def __repr__(self):
              return 'Vector (%d, %d)' % (self.a, self.b)
      
          def __add__(self,other):
              if other.__class__ is Vector:
                  return Vector(self.a + other.a, self.b + other.b)
              elif other.__class__ is int:
                  return Vector(self.a+other,self.b)
      
          def __radd__(self,other):
              """反向算术运算符的重载
              __add__运算符重载可以保证V+int的情况下不会报错,但是反过来int+V就会报错,通过反向运算符重载可以解决此问题
              """
      
              if other.__class__ is int or other.__class__ is float:
                  return Vector(self.a+other,self.b)
              else:
                  raise ValueError("值错误")
      
          def __iadd__(self,other):
              """复合赋值算数运算符的重载
              主要用于列表,例如L1+=L2,默认情况下调用__add__,会生成一个新的列表,
              当数据过大的时候会影响效率,而此函数可以重载+=,使L2直接增加到L1后面
              """
      
              if other.__class__ is Vector:
                  return Vector(self.a + other.a, self.b + other.b)
              elif other.__class__ is int:
                  return Vector(self.a+other,self.b)
      v1 = Vector(2,10)
      v2 = Vector(5,-2)
      print (v1 + v2)
      print (v1+5)
      print (6+v2)
      

    关于 name

    首先需要了解 name 是属于 python 中的内置类属性,就是它会天生就存在于一个 python 程序中,代表对应程序名称。

    比如所示的一段代码里面(这个脚本命名为 pcRequests.py),我只设了一个函数,但是并没有地方运行它,所以当 run 了这一段代码之后我们有会发现这个函数并没有被调用。但是当我们在运行这个代码时这个代码的 name 的值为 main (一段程序作为主线运行程序时其内置名称就是 main)。

    import requests
    class requests(object):
        def __init__(self,url):
            self.url=url
            self.result=self.getHTMLText(self.url)
        def getHTMLText(url):
            try:
                r=requests.get(url,timeout=30)
                r.raise_for_status()
                r.encoding=r.apparent_encoding
                return r.text
            except:
                return "This is a error."
    print(__name__)
    

    结果:

    __main__
    Process finished with exit code 0
    

    当这个 pcRequests.py 作为模块被调用时,则它的 name 就是它自己的名字:

    import pcRequestspcRequestsc=pcRequestsc.__name__
    

    结果:

    'pcRequests'
    

    看到这里应该能明白,自己的 name 在自己用时就是 main,当自己作为模块被调用时就是自己的名字,就相当于:我管自己叫我自己,但是在朋友眼里我就是小仙女一样

    Python3 类方法总结

    • 普通方法:对象访问
    • 私有方法:两个下划线开头,只能在类内部访问
    • 静态方法:类和对象访问,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
    • 类方法:类和对象访问,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
    • 多继承情况下:从左到右查找方法,找到为止,不然就抛出异常
    class People:
    
        # 定义基本属性
        name=''
        age=0
        # 定义私有属性外部无法直接访问
        __weight=0
        def __init__(self,n,a,w):
            self.name = n
            self.age = a
            self.__weight = w
        def speak(self):
            print("%s say : i am %d."%(self.name,self.age))
    p = People('Python',10,20)
    p.speak()
    # __weight无法直接访问
    print(p.name,'--',p.age)#,'--',p.__weight)
    

    继承

    单继承:

    class Student(People):
        grade=''
        def __init__(self,n,a,w,g):
            People.__init__(self,n,a,w)
            self.grade = g
    
        # 覆写父类方法
        def speak():
            print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
    
    class Speak():
        topic=''
        name=''
        def __init__(self,n,t):
            self.name = n
            self.topic = t
        # 普通方法,对象调用
        def speak(self):
            print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
    
        # 私有方法,self调用
        def __song(self):
            print('唱一首歌自己听',self);
    
        # 静态方法,对象和类调用,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
        @staticmethod
        def song():
            print('唱一首歌给类听:静态方法');
    
        # 普通方法,对象调用
        def song(self):
            print('唱一首歌给你们听',self);
            
        # 类方法,对象和类调用,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
        @classmethod
        def song(self):
            print('唱一首歌给类听:类方法',self)
    

    多继承:

    class Sample(Speak,Student):
        a = ''
        def __init__(self,n,a,w,g,t):
            Student.__init__(self,n,a,w,g)
            Speak.__init__(self,n,t)
    test = Sample('Song',24,56,7,'Python')
    test.speak()
    test.song()
    Sample.song()
    Sample.song()
    test.song()
    # test.__song() 无法访问私有方法
    

    在 Python 中,方法分为三类实例方法、类方法、静态方法。三者区别看代码如下:

    class Test(object):
        def InstanceFun(self):
            print("InstanceFun");
            print(self);
        @classmethod
        def ClassFun(cls):
            print("ClassFun");
            print(cls);
        @staticmethod
        def StaticFun():
            print("StaticFun");
    
    t = Test();     
    t.InstanceFun();   # 输出InstanceFun,打印对象内存地址“<__main__.Test object at 0x0293DCF0>”
    Test.ClassFun();     # 输出ClassFun,打印类位置 <class '__main__.Test'>
    Test.StaticFun();    # 输出StaticFun
    t.StaticFun();       # 输出StaticFun
    t.ClassFun();        # 输出ClassFun,打印类位置 <class '__main__.Test'>
    Test.InstanceFun();     # 错误,TypeError: unbound method instanceFun() must be called with Test instance as first argument
    Test.InstanceFun(t);    # 输出InstanceFun,打印对象内存地址“<__main__.Test object at 0x0293DCF0>”
    t.ClassFun(Test);       # 错误   classFun() takes exactly 1 argument (2 given)   
    

    类的二元方法运算符重载介绍的并不全面,文中介绍的全是正向方法,其实还有反向方法,就地方法。下面补充一些。

    当解释器碰到 a+b 时,会做以下事情:

    从 a 类中找 add 若返回值不是 NotImplemented, 则调用 a.add(b)

    若 a 类中没有 add 方法,则检查 b 有没有 radd 。如果如果有,则调用 b.radd(a),若没有,则返回 NotImplemented。

    接上条,若 b 也没有 radd 方法,则抛出 TypeError,在错误消息中知名操作数类型不支持。

    比如:向量类 应当有向量与整数的乘法:

    >>>a = Myvector([1,2,3])
    >>>print(a.value)
    [1,2,3]
    >>>b=3
    >>>c = a*b   #此时调用Myvector.__mul__()
    >>>print(c.value)
    [3,6,9]
    >>> d=b*a  #这句会出错。
    

    期望得到 b*a 也返回一个向量,ba 应该等于 ab。此时就需要在 Myvector 类中定义一个__rmul__方法。

    def __rmul__(self, other):
        if isinstance(other, int):
            return Myvector([a*other for a in self.m])
    

    每个运算符都有正向方法重载,反向方法重载。有一些有就地方法(即不返回新的对象,而是修改原本对象)。

    __str__函数

    str 是一个类的方法,在打印类对象,获取其属性信息时调用。打印一个实例化对象时,默认打印的其实时一个对象的地址,但是我们可以对其进行重载,打印我们想要的信息。例如上面的例子中进行的重载。

  • 10、Python3 命名空间和作用域

    先看看官方文档的一段话:

    A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。

    命名空间(Namespace)是从名称到对象的映射,大部分的命名空间都是通过 Python 字典来实现的。

    命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。

    一般有三种命名空间:

    • 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
    • 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
    • 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)

    img

    命名空间查找顺序:

    假设我们要使用变量 runoob,则 Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间

    命名空间的生命周期:

    命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。

    作用域

    A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

    作用域就是一个 Python 程序可以直接访问命名空间的正文区域。

    在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

    Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

    变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

    有四种作用域:

    • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
    • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
    • G(Global):当前脚本的最外层,比如当前模块的全局变量。
    • B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

    规则顺序: L –> E –> G –>gt; B

    在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

    内置作用域是通过一个名为 builtin 的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。在Python3.0中,可以使用以下的代码来查看到底预定义了哪些变量:

    全局变量和局部变量

    定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。

    局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。

    global 和 nonlocal关键字

    当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。

    如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了

  • 11、Python3 标准库概览

    操作系统接口

    os模块提供了不少与操作系统相关联的函数。

    建议使用 “import os” 风格而非 “from os import *”。这样可以保证随操作系统不同而有所变化的 os.open() 不会覆盖内置函数 open()。

    在使用 os 这样的大型模块时内置的 dir() 和 help() 函数非常有用:

    针对日常的文件和目录管理任务,:mod:shutil 模块提供了一个易于使用的高级接口:

    文件通配符

    glob模块提供了一个函数用于从目录通配符搜索中生成文件列表:

    命令行参数

    通用工具脚本经常调用命令行参数。这些命令行参数以链表形式存储于 sys 模块的 argv 变量。

    错误输出重定向和程序终止

    sys 还有 stdin,stdout 和 stderr 属性,即使在 stdout 被重定向时,后者也可以用于显示警告和错误信息。

    字符串正则匹配

    re模块为高级字符串处理提供了正则表达式工具。对于复杂的匹配和处理,正则表达式提供了简洁、优化的解决方案:

    >>> import re
    >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
    ['foot', 'fell', 'fastest']
    >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
    'cat in the hat'
    

    果只需要简单的功能,应该首先考虑字符串方法,因为它们非常简单,易于阅读和调试:

    >>> 'tea for too'.replace('too', 'two')
    'tea for two'
    

    数学

    math模块为浮点运算提供了对底层C函数库的访问:

    random提供了生成随机数的工具。

    >>> import random
    >>> random.choice(['apple', 'pear', 'banana'])
    'apple'
    >>> random.sample(range(100), 10)   # sampling without replacement
    [30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
    >>> random.random()    # random float
    0.17970987693706186
    >>> random.randrange(6)    # random integer chosen from range(6)
    4
    

    访问 互联网

    有几个模块用于访问互联网以及处理网络通信协议。其中最简单的两个是用于处理从 urls 接收的数据的 urllib.request 以及用于发送电子邮件的 smtplib:

    >>> from urllib.request import urlopen
    >>> for line in urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl'):
    ...     line = line.decode('utf-8')  # Decoding the binary data to text.
    ...     if 'EST' in line or 'EDT' in line:  # look for Eastern Time
    ...         print(line)
    
    <BR>Nov. 25, 09:43:32 PM EST
    
    >>> import smtplib
    >>> server = smtplib.SMTP('localhost')
    >>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
    ... """To: jcaesar@example.org
    ... From: soothsayer@example.org
    ...
    ... Beware the Ides of March.
    ... """)
    >>> server.quit()
    

    日期和时间

    datetime模块为日期和时间处理同时提供了简单和复杂的方法。

    支持日期和时间算法的同时,实现的重点放在更有效的处理和格式化输出。

    该模块还支持时区处理:

    >>> # dates are easily constructed and formatted
    >>> from datetime import date
    >>> now = date.today()
    >>> now
    datetime.date(2003, 12, 2)
    >>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
    '12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'
    
    >>> # dates support calendar arithmetic
    >>> birthday = date(1964, 7, 31)
    >>> age = now - birthday
    >>> age.days
    14368
    

    数据压缩

    以下模块直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile。

    >>> import zlib
    >>> s = b'witch which has which witches wrist watch'
    >>> len(s)
    41
    >>> t = zlib.compress(s)
    >>> len(t)
    37
    >>> zlib.decompress(t)
    b'witch which has which witches wrist watch'
    >>> zlib.crc32(s)
    226805979
    

    性能度量

    有些用户对了解解决同一问题的不同方法之间的性能差异很感兴趣。Python 提供了一个度量工具,为这些问题提供了直接答案。

    例如,使用元组封装和拆封来交换元素看起来要比使用传统的方法要诱人的多,timeit 证明了现代的方法更快一些。

    >>> from timeit import Timer
    >>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
    0.57535828626024577
    >>> Timer('a,b = b,a', 'a=1; b=2').timeit()
    0.54962537085770791
    

    相对于 timeit 的细粒度,:mod:profile 和 pstats 模块提供了针对更大代码块的时间度量工具。

    测试模块

    开发高质量软件的方法之一是为每一个函数开发测试代码,并且在开发过程中经常进行测试

    doctest模块提供了一个工具,扫描模块并根据程序中内嵌的文档字符串执行测试。

    测试构造如同简单的将它的输出结果剪切并粘贴到文档字符串中。

    通过用户提供的例子,它强化了文档,允许 doctest 模块确认代码的结果是否与文档一致:

    def average(values):
        """Computes the arithmetic mean of a list of numbers.
    
        >>> print(average([20, 30, 70]))
        40.0
        """
        return sum(values) / len(values)
    
    import doctest
    doctest.testmod()   # 自动验证嵌入测试
    

    unittest模块不像 doctest模块那么容易使用,不过它可以在一个独立的文件里提供一个更全面的测试集:

    import unittest
    
    class TestStatisticalFunctions(unittest.TestCase):
    
        def test_average(self):
            self.assertEqual(average([20, 30, 70]), 40.0)
            self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
            self.assertRaises(ZeroDivisionError, average, [])
            self.assertRaises(TypeError, average, 20, 30, 70)
    
    unittest.main() # Calling from the command line invokes all tests
    

    关于urlopen的补充

    #处理get请求,不传data,则为get请求
    
    import urllib
    from urllib.request import urlopen
    from urllib.parse import urlencode
    
    url='http://www.xxx.com/login'
    data={"username":"admin","password":123456}
    req_data=urlencode(data)#将字典类型的请求数据转变为url编码
    res=urlopen(url+'?'+req_data)#通过urlopen方法访问拼接好的url
    res=res.read().decode()#read()方法是读取返回数据内容,decode是转换返回数据的bytes格式为str
    
    print(res)
    #处理post请求,如果传了data,则为post请求
    
    import urllib
    from urllib.request import Request
    from urllib.parse import urlencode
    
    url='http://www.xxx.com/login'
    data={"username":"admin","password":123456}
    data=urlencode(data)#将字典类型的请求数据转变为url编码
    data=data.encode('ascii')#将url编码类型的请求数据转变为bytes类型
    req_data=Request(url,data)#将url和请求数据处理为一个Request对象,供urlopen调用
    with urlopen(req_data) as res:
        res=res.read().decode()#read()方法是读取返回数据内容,decode是转换返回数据的bytes格式为str
    
    print(res)
    

    时间和日期补充

    常用时间处理方法

    • 今天 today = datetime.date.today()
    • 昨天 yesterday = today - datetime.timedelta(days=1)
    • 上个月 last_month = today.month - 1 if today.month - 1 else 12
    • 当前时间戳 time_stamp = time.time()
    • 时间戳转datetime datetime.datetime.fromtimestamp(time_stamp)
    • datetime转时间戳 int(time.mktime(today.timetuple()))
    • datetime转字符串 today_str = today.strftime("%Y-%m-%d")
    • 字符串转datetime today = datetime.datetime.strptime(today_str, "%Y-%m-%d")
    • 补时差 today + datetime.timedelta(hours=8)
  • 12、Python3实例

  • Python3 实例

    以下实例在 Python3.4.3 版本下测试通过:

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值