• python基础


    主要参考廖雪峰

    安装参考:Windows 系统安装 Python 3.8 详解

    一、介绍

    面向对象、解释型语言,跨平台的脚本语言。

    可以做 日常任务(eg  自动备份MP3)、做网站(eg youtube、豆瓣)、做网络游戏的后台。

    python为我们提供了非常完美的代码库,包括网络、文件、gui、数据库、文本等大量内容。

    定位:优雅、明确、简单。代码越少,开发效率越高。

    python的好处:

    1、学习容易

    Python是一门比较容易学习的语言,因为它是非常高级的语言,比C和C++这样的语言,还要高级几个层次。它不需要管理内存分配,不需要定义变量的类型即可使用,内置了很多数据类型直接使用,而不需要考虑怎么样创建这些类型,比如列表、字典、字符串这样高级的功能。

    另外,用它写出的代码,可以直接运行,不需要进行编译的操作。

    还有一点,用它写出的代码非常短。

    2、开发效率高

    Python是一门开发效率最高的语言,它比C有6倍的开发效率,简单来说,如果一个C开发人员工作6天,使用python的开发人员只需要工作一天即可,意味着做Python开发人员可一周只上一天班。

    它比C++有2倍的开发效率,它比Java和C#也有1.5倍的开发效率。有这么高的开发效率,当然是用性能换来的代价,不过从目前硬件技术进步来看,目前的CPU计算能力普遍是过剩的,并且越来越多硬件成本降低,但人工的成本越来越贵。其实从社会进步来看,也是工具越来越先进,但人的大脑管理复杂程度并没有跟着提高,显然要提高起来就比较困难了。

    目前在嵌入式系统都慢慢走向多核的CPU,在手机方面,都已经进入64位的8核时代了。在嵌入式系统方面,也有Pyboard这样的开源项目来进行了,这样的嵌入式Python主要适用于机器人控制方面。

    3、调试运行方便

    无论是在Windows平台,还是Linux平台,都一样开发和调试。跨平台运行更加方便,如果没有使用平台差别的API接口,只要写一遍代码,就可以在Windows平台或Linux平台上运行。

    4、开源免费

    Python无论在商业上,还是教育上,都是免费使用,意味可以零成本进入学习它,使用它。Python拥有众多功能完善的开发库可以使用。

    5、测试领域需求

    测试是软件开发里有相当大的工作量,比如模块测试,当开发人员把一个模块功能完成之后,需要测试这个模块是否正确,就需要搭建一堆测试代码,才可以验证的。这时,如果使用C++或Java来写这些功能,显然没有使用Python来得快,从前面效率就可以看到。

    因此,通常就会变成这样的开发模式:发布的软件是使用C++或Java开发,但测试的代码使用Python来开发。

    比如嵌入式系统涉及网络通讯方面,需要不断地向嵌入式系统发送网络数据和接收网络数据,就可以使用Python搭建一个测试环境出来,这样花费很少的时间,就可以对嵌入式系统进行验证,提高代码的质量,减少嵌入式系统与其它系统的调试时间,以及以后维护时间。

    另外,通过使用Python语言编写众多的脚本,就可以提高自动化测试水平,每发布一个版本,就可以把以前的测试用例,全自动化测试一遍,这样会大大提高对软件快速发布的要求。像我所见过的测试用例,就有几万个,如果靠手工方式测试,验证起来是非常慢的,并且并不全面。目前采用全自动化测试之后,每天24小时运行,一台电脑相当于10个测试员工的工作量,可见带来多大效率的提升。在可以预见的将来,在测试领域里,Python的工作需求会持续增加,要求所有测试人员都会掌握这个好用的工具。

    二、.py文件

    • windows中不能像.exe文件那样直接运行.py文件。
    • linux中可以,只需在文件最上方加一行 #!/usr/bin/env python3,执行时直接 ./01.py   否则就需要python 01.py

    参考:#!/usr/bin/env python 有什么用?

    第一部分是 #! (叫做Sha-bang,有的翻译组将它译作 释伴,即“解释伴随行”的简称。后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。)

    第二部分是 /usr/bin/env python

    执行python程序的三种方式

    1、解释器:cpython 解释器,将程序代码转换成cpu能够执行的机器码,装完python后就有

    2、交互式:ipython软件,执行类似1,都是在终端中 python 01.py

    3、IDE:pycharm中执行  【我的Pycharm,我做主

    三、基础

    1、注释

      单行注释 #  多行注释 """(3个引号)

    2、大小写敏感,语句结尾分号可以省略,主要通过换行来识别语句的结束

    3、4个空格(tab)的缩进

    4、数据类型:

    • 整数(大小没有限制,超出一定范围为inf 无限大)
    • 浮点数(除法的结果是浮点数,9/3=3.0 。地板除则是返回整数9//3=3,整数的地板除结果永远是整数 即使除不尽
    • 字符串
    • 布尔值(True、False)
    • 空值(None)

    5、变量:大小写英文、数字和_   ,但不能数字开头。【py文件也不要以数字开头】

      查看一个变量的类型用type()

    6、字符串(str、bytes)

      在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,print('包含中文的str')

      对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

     eg:ord('A')=65  chr(25991)='文'

    Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes

    Python对bytes类型的数据用带b前缀的单引号或双引号表示:x = b'ABC'

    要计算str包含多少个字符,可以用len()函数。如果换成byteslen()函数就计算字节数

    Python 3 字符串中的 STR 和 Bytes 究竟有什么区别

    • Python2的字符串有两种:str和Unicode,Python3的字符串也有两种:str和Bytes
    • Python2里面的str和Unicode是可以混用的,在都是英文字母的时候str和unicode没有区别。
    • Python3严格区分文本(str)和二进制数据(Bytes),文本总是Unicode,用str类型,二进制数据则用Bytes类型表示。

     

    由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    

    第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

    第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

    格式化

    在Python中,采用的格式化方式和C语言是一致的,用%实现。

    >>> 'Hello, %s' % 'world'
    'Hello, world'
    >>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
    'Hi, Michael, you have $1000000.'

    %运算符就是用来格式化字符串的

    小数点:保留两位有效小数

    >>> a = 12.345
    >>> print("%.2f" % a) 

    7、高级变量类型

    • list 列表,有序集合 []
    classmates = ['Michael', 'Bob', 'Tracy']

    list是个可变列表 [ ] ,元素类型可以不同。索引从0开始,最后一个元素的索引是len(classmates) - 1 。如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素

    添加元素:追加到末尾 append()    添加到指定位置 insert(index,str)

    删除元素:删除末尾元素  pop()     删除指定位置的元素,用pop(i)方法,其中i是索引位置

    list元素也可以是另一个list,eg:s = ['python', 'java', ['asp', 'php'], 'scheme']

    • tuple元组,有序列表 ()

    一经定义 元素不能改变,代码更安全,元素类型可以不同。如果可能,能用tuple代替list就尽量用tuple。

    classmates = ('Michael', 'Bob', 'Tracy')

    定义只有1个元素的tuple定义时必须加一个逗号,  t = (1,)

    元组添加元素,其实是新建一个元组:

    new_classmates =classmates +(5,)

    8、循环

     Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来

     Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list.

    range(101)就可以生成0-100的整数序列.

    sum = 0
    for x in range(101):
        sum = sum + x
    print(sum)

    for循环其实可以同时使用两个甚至多个变量,比如dictitems()可以同时迭代key和value:

    >>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
    >>> for k, v in d.items():
    ...     print(k, '=', v)
    ...
    y = B
    x = A
    z = C

    第二种循环是while循环

    9、dict 字典 {}

     Python内置了字典:dict的支持,dict全称dictionary

    d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}

    判断一个key是否存在:一是通过in判断key是否存在;二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:

    'Thomas' in d  不存在则返回 False

    d.get('Thomas') 或者 d.get('Thomas',-1)

    dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象

    在Python中,字符串、整数、None空对象 等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key

    在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象

    10、set 集合

      set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key,无序的不重复元素序列。

    要创建一个set,需要提供一个list作为输入集合:s = set([1, 2, 3])

    可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

    四、函数

    1、定义函数

     使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。eg:

    def my_abs(x):
        if x >= 0:
            return x
        else:
            return -x

    返回多个值,直接在return中定义 return nx, ny   

    x, y = move(100, 100, 60, math.pi / 6)

    但其实这只是一种假象,Python函数返回的仍然是单一值:原来返回值是一个tuple

     r = move(100, 100, 60, math.pi / 6)
    print(r)=(151.96152422706632, 70.0)

    2、函数的参数

    • 位置参数:

    def power(x, n): 函数有两个参数:xn,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数xn

    • 默认参数:

    def power(x, y, n=2):设置默认参数时,有几点要注意:

    一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);

    因为会产生歧义。例如 def foo(p1, p2=6, p3)  若调用函数foo(1, 2),系统会调用foo(1, 2)其中p1 = 1 p2重新复制成了2 p2 = 2,第三个位置参数就会不见了,这时候系统就会报出错了

    二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小(不怎么变化)的参数放后面。变化小的参数就可以作为默认参数。

    使用默认参数有什么好处?最大的好处是能降低调用函数的难度

    注意:定义默认参数要牢记一点:默认参数必须指向不变对象

    • 可变参数

    可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。eg:

    def calc(*numbers):
        sum = 0
        for n in numbers:
            sum = sum + n * n
        return sum

    调用 calc(1,2,3)   

    而允许把list或tuple的元素变成可变参数传进去:

    nums = [1, 2, 3]    calc(*nums)

    *nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

    五、高级特性

    在Python中,代码不是越多越好,而是越少越好。代码不是越复杂越好,而是越简单越好。

    基于这一思想,我们来介绍Python中非常有用的高级特性,1行代码能实现的功能,决不写5行代码。

    切片

    取一个list或tuple的部分元素是非常常见的操作。取前3个元素,应该怎么做?

    可以 [L[0], L[1], L[2]]  或者用循环,但都太麻烦

    Python提供了切片(Slice)操作符,能大大简化这种操作:L[0:3]  从索引0开始取,直到索引3为止,但不包括索引3。索引0可以省略,L[:3]

    每两个取一个:L[:10:2]

    字符串也有切片操作

    总结:

    切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的结束位置,为列表末位时可省略;m 可以不提供,默认值是1,不允许为0 ,当m为负数时,列表翻转。注意:这些值都可以大于列表长度,不会报越界。

    切片的基本含义是:从序列的第i位索引起,向右取到后n位元素为止,按m间隔过滤 。

    迭代

    如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。

    还可以作用在其他可迭代对象上。eg dict字典、str字符串

    >>> d = {'a': 1, 'b': 2, 'c': 3}
    >>> for key in d:
    ...     print(key)
    ...
    a
    c
    b

     默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()

    >>> for ch in 'ABC':
    ...     print(ch)
    ...
    A
    B
    C

    那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
    >>> isinstance([1,2,3], Iterable) # list是否可迭代
    True
    >>> isinstance(123, Iterable) # 整数是否可迭代
    False

    还有,同时引用两个变量,在Python里是很常见的,比如下面的代码:

    >>> for x, y in [(1, 1), (2, 4), (3, 9)]:
    ...     print(x, y)
    ...
    1 1
    2 4
    3 9

    列表生成式

    运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list

    例如,list(rang(1,11)) 

    >>> list(range(1, 11))
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    >>> [x * x for x in range(1, 11)]
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

    写列表生成式时,把要生成的元素表达式(x * x)放到前面,后面跟for循环,就可以把list创建出来,十分有用.

    for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

    >>> [x * x for x in range(1, 11) if x % 2 == 0]
    [4, 16, 36, 64, 100]

    还可以使用两层循环,可以生成全排列:

    >>> [m + n for m in 'ABC' for n in 'XYZ']
    ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

    例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

    >>> import os # 导入os模块,模块的概念后面讲到
    >>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
    ['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads']

    把一个list中所有的字符串变成小写:

    >>> L = ['Hello', 'World', 'IBM', 'Apple']
    >>> [s.lower() for s in L]
    ['hello', 'world', 'ibm', 'apple']

    注意:如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()方法,所以列表生成式会报错,

    正确做法,使用内建的isinstance函数可以判断一个变量是不是字符串

    >>> x = 'abc'
    >>> y = 123
    >>> isinstance(x, str)
    True
    >>> isinstance(y, str)
    False
    L1 = ['Hello', 'World', 18, 'Apple', None]
    L2 = [x.lower() for x in L1 if isinstance(x,str)]
    # 测试:
    print(L2)
    if L2 == ['hello', 'world', 'apple']:
        print('测试通过!')
    else:
        print('测试失败!')
    View Code

    生成器与yield

    1、通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator

    2、要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

    >>> g = (x * x for x in range(10))
    >>> g
    <generator object <genexpr> at 0x1022ef630>

    如果要一个一个打印出generator的元素来,可以通过next()函数获得generator的下一个返回值,即 next(g)

    3、generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    不过,基本上永远不会调用next(),而是通过for循环来迭代(因为generator也是可迭代对象)它,并且不需要关心StopIteration的错误。

    >>> g = (x * x for x in range(10))
    >>> for n in g:
    ...     print(n)

    4、generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

    比如,著名的斐波拉契数列(Fibonacci),可用普通函数实现也可以用generator实现。

    #!/usr/bin/env python
    # encoding: utf-8
    
    # file: Fibonacci.py
    
    # 斐波拉契数列
    #著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
    #1, 1, 2, 3, 5, 8, 13, 21, 34, ...
    
     # 普通函数
    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a + b  # 先计算右边,再赋值
            n = n + 1
        return 'done'
    
    print(fib(1))
    print(fib(3))
    print(fib(6))
    
    
    # 生成器,,只需要把print(b)改为yield b
    def fibg(max):
        n, a, b = 0, 0, 1
        while n < max:
            yield b
            a, b = b, a + b  # 先计算右边,再赋值
            n = n + 1
        return 'done'
    
    f = fibg(6)
    # 我们在循环过程中不断调用yield,就会不断中断。
    # 当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
    for n in fibg(6):
        print(n)
    View Code

    最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

    请注意区分普通函数和generator函数,普通函数调用直接返回结果;

    generator函数的“调用”实际返回一个generator对象。

    yield与return 的区别

    • return:就是在程序中返回某个值,返回之后程序就不再往下运行了。
    • yield:把它看做一个是生成器(generator)的一部分(带yield的函数才是真正的迭代器)

    带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

    迭代器

    迭代对象(ITerable):可以直接作用于for循环的对象,注意有两类

    • 一类是集合数据类型,如listtupledictsetstr等;
    • 一类是generator,包括生成器和带yield的generator function。

    迭代器 (ITerator):可以被next()函数调用并不断返回下一个值的对象【它表示的是一个惰性计算的序列】,只有一类

    • 生成器generator是迭代器

     可以使用isinstance()判断一个对象是否是Iterator对象:

    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True

    listdictstrIterable变成Iterator可以使用iter()函数

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    你可能会问,为什么listdictstr等数据类型不是Iterator

    这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

    Python的for循环本质上就是通过不断调用next()函数实现的,例如:

    for x in [1, 2, 3, 4, 5]:
        pass
    

    实际上完全等价于:

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

    六、模块

    1、介绍

    在Python中,一个.py文件就称之为一个模块(Module)。

    好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。

    使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,但是也要注意,尽量不要与内置函数名字冲突。点这里查看Python的所有内置函数。

    你也许还想到,如果不同的人编写的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。也就是模块的父目录

    请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是mycompany

    创建自己的模块时,要注意:

    • 模块名要遵循Python变量命名规范,不要使用中文、特殊字符
    • 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。

    2、使用模块

    以内建的sys模块为例,编写一个hello的模块:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    ' a test module '
    
    __author__ = 'Michael Liao'
    
    import sys
    
    def test():
        args = sys.argv
        if len(args)==1:
            print('Hello, world!')
        elif len(args)==2:
            print('Hello, %s!' % args[1])
        else:
            print('Too many arguments!')
    
    if __name__=='__main__':
        test()
    View Code

    注意:代码中的下划线都是两根

    第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;

    第6行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

    以上就是Python模块的标准文件模板,当然也可以全部删掉不写,但是,按标准办事肯定没错。

    import sys  导入sys模块后,我们就有了变量sys指向该模块,利用sys这个变量,就可以访问sys模块的所有功能。

    也可以: from 模块名 import 变量或者函数 ,eg:from math import pi   代码中就可以直接使用pi变量了【而不用写math.pi 】。也可以from math import *

    最后,注意到这两行代码:

    if __name__=='__main__':
        test()

    当我们命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__;而如果在其他地方导入该hello模块时,这个if判断将失败,不会执行test()方法。

    【__name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。】

    因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。参考下文

    3、模块作用域

    外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

    正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI等;

    类似【字母前后是双下划线】__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;

    类似【字母前是单/双下划线】_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

    4、安装第三方模块

    以前用pip,现在推荐直接使用Anaconda【开源的Python包管理器】,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。

    可以从Anaconda官网下载GUI安装包,安装包有500~600M,所以需要耐心等待下载。网速慢的同学请移步国内镜像。下载后直接安装,Anaconda会把系统Path中的python指向自己自带的Python,并且,Anaconda安装的第三方模块会安装在Anaconda自己的路径下,不影响系统已安装的Python目录。

    安装好Anaconda后,重新打开命令行窗口,输入python,可以看到Anaconda的信息。

    5、import与from...import的区别

    彻底搞懂Python 中的 import 与 from import

    Python 导入其他模块的方式:

    • 什么时候用import xxx
    • 什么时候用from xxx import yyy
    • 什么时候用from xxx.yyy import zzz
    • 什么时候用from xxx import *

    1、系统自带的模块

    以正则表达式模块re为例:

    import re
    target = 'abc1234xyz'
    re.search('(d+)', target)
    from re import search
    target = 'abc1234xyz'
    search('(d+)', target)

    查看他们的类型

    >>> import re
    >>> type(re)
    <class 'module'>
    >>> from re import search
    >>> type(search)
    <class 'function'>

    可以看到,直接使用import re导入的re它是一个module类,也就是模块。我们把它成为正则表达式模块。而当我们from re import search时,这个search是一个function类,我们称呼它为search 函数

    一个模块里面可以包含多个函数。

    如果在你的代码里面,你已经确定只使用search函数,不会再使用正则表达式里面的其他函数了,那么你使用两种方法都可以,没什么区别。但是,如果你要使用正则表达式下面的多个函数,或者是一些常量,那么用第一种方案会更加简洁清晰。

    2、第三方模块

    例如lxml它既能处理xml的数据,又能处理html的数据,于是这种库会划分子模块,lxml.html模块专门负责html相关的数据。

    你可以:from lxml import html

    但是不能 import lxml

    注:你只能导入一个模块或者导入一个函数或者类,你不能导入一个文件夹 

    无论你使用的是import xxx还是from xxx.yyy.zzz.www import qqq,你导入进来的东西,要不就是一个模块(对应到.py 文件的文件名),或者是某个.py 文件中的函数名、类名、变量名。

    无论是import xxx还是from xxx import yyy,你导入进来的都不能是一个文件夹的名字。

    总结:

    • 无论是使用import还是from import,第一个要求是代码能够正常运行,其次,根据代码维护性,团队编码风格来确定选择哪一种方案。
    • 如果我们只会使用到某个模块下面的一个函数(或者常量、类)并且名字不会产生混淆,可识别性高,那么from 模块名 import 函数名这没有什么问题。
    • 如果我们会用到一个模块下面的多个函数,或者是我们将要使用的函数名、常量名、类名可能会让人产生混淆(例如 re.S、re.I),那么这种情况下,import 模块名 然后再 模块名.xxx来调用会让代码更加清晰,更好维护。
    • 但无论什么情况下,都禁止使用from xxx import * 这种写法,它会给你带来无穷无尽的噩梦。【因为不知道你导入进了啥,写程序这个事情,几乎在任何情况下,白名单都优于黑名单。白名单是可控的,黑名单是总会有漏网之鱼的。】

    七、程序入口与__name__

    1、程序入口

    对于很多编程语言来说,程序都必须要有一个入口,比如 C,C++,以及完全面向对象的编程语言 Java,C# 等。C 和 C++ 都需要有一个 main 函数来作为程序的入口,同样,Java 和 C# 必须要有一个包含 Main 方法的主类来作为程序入口。

    Python 则有不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口

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

    举一个例子来说明一下,假设我们有一个 const.py 文件,内容如下:

    PI = 3.14
    
    def main():
        print "PI:", PI
    
    main()
    View Code

    我们在这个文件里边定义了一些常量,然后又写了一个 main 函数来输出定义的常量,最后运行 main 函数就相当于对定义做一遍人工检查,看看值设置的都对不对。然后我们直接执行该文件(python const.py),输出:

    PI: 3.14
    

    现在,我们有一个 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()
    View Code

    运行 area.py,输出结果:

    PI: 3.14
    round area:  12.56
    

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

    PI = 3.14
    
    def main():
        print "PI:", PI
    
    if __name__ == "__main__":
        main()
    View Code

    然后再运行 area.py,输出如下:

    round area:  12.56
    

    再运行下 const.py,输出如下:

    PI: 3.14
    

    这才是我们想要的效果。

    if __name__ == '__main__' 就相当于是 Python 模拟的程序入口Python 本身并没有规定这么写,这只是一种编码习惯。由于模块之间相互引用,不同模块可能都有这样的定义,而入口程序只能有一个。

    到底哪个入口程序被选中,这取决于 __name__ 的值。

    2、__name__

      __name__ 是内置变量,用于表示当前模块的名字,同时还能反映一个包的结构。来举个例子,假设有如下一个包:

    a
    ├── b
    │   ├── c.py
    │   └── __init__.py
    └── __init__.py
    

    目录中所有 py 文件的内容都为:

    print __name__
    

    我们执行 python -c "import a.b.c", 【-c 指定要执行的命令】输出结果:

    a
    a.b
    a.b.c
    

    由此可见,__name__ 可以清晰的反映一个模块在包中的层次。其实,所谓模块名就是 import 时需要用到的名字,例如:

    import tornado
    import tornado.web
    

    这里的 tornado 和 tornado.web 就被称为模块的模块名。

    如果一个模块被直接运行,则其没有包结构,其 __name__ 值为 __main__。例如在上例中,我们直接运行 c.py 文件(python a/b/c.py),输出结果如下:

    __main__
    

    而被其它模块导入执行的值为该模块(a.py)的名字a

    所以,if __name__ == '__main__' 我们简单的理解就是: 如果模块是被直接运行的,则代码块被运行,如果模块是被导入的,则代码块不被运行

  • 相关阅读:
    删除 SQL Server 2005 Express 工具
    静态和非静态
    C#中的托管和非托管
    类和结构的区别
    asp.net URL DES加密 什在URL中的使用
    正则替换图片路径
    Oracle 正则 一行转多行
    Oracle 存储过程
    HTTP SOAP Request
    jquery 高亮
  • 原文地址:https://www.cnblogs.com/peterYong/p/10926510.html
Copyright © 2020-2023  润新知