美文网首页
2023-02-01 Python 3基础四

2023-02-01 Python 3基础四

作者: heybo_zhang | 来源:发表于2023-01-30 15:22 被阅读0次

    Python 3基础四

    Python3 函数

    函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
    函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
    个人理解,函数或者方法就是定义好的,直接拿来用就可以,不用关注实现方式,拿到想要的结果就ok,跟对外访问接口类似。
    当然,如果可以频繁的用到某些代码,就把代码封装起来,一次书写,多次调用。
    定义一个函数
    你可以定义一个由自己想要功能的函数,以下是简单的规则:
    函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
    任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
    函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
    函数内容以冒号 : 起始,并且缩进。
    return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。
    Python 定义函数使用 def 关键字,一般格式如下:

    # Python 定义函数使用 def 关键字,一般格式如下:
    def 函数名(参数列表):
        函数体
    # JAVA 
    public static void  函数名(参数列表){
           函数体
    }
    
    #!/usr/bin/python3
    
    def hello() :
        print("Hello World!")
    
    hello()
    
    public static void  hello(){
           System.out.println("Hello World!")
    }
    
    # java 入口函数
    public static void main(){
     hello()
    }
    
    image.png
    #!/usr/bin/python3
     
    def max(a, b):
        if a > b:
            return a
        else:
            return b
     
    a = 4
    b = 5
    print(max(a, b))
    
    # 计算面积函数
    def area(width, height):
        return width * height
     
    def print_welcome(name):
        print("Welcome", name)
     
    print_welcome("Runoob")
    w = 4
    h = 5
    print("width =", w, " height =", h, " area =", area(w, h))
    
    5
    Welcome Runoob
    width = 4  height = 5  area = 20
    

    函数调用

    定义一个函数:给了函数一个名称,指定了函数里包含的参数,和代码块结构。
    这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从 Python 命令提示符执行。
    如下实例调用了 printme() 函数:

    #!/usr/bin/python3
     
    # 定义函数
    def printme( str ):
       # 打印任何传入的字符串
       print (str)
       return
     
    # 调用函数
    printme("我要调用用户自定义函数!")
    printme("再次调用同一函数")
    
    我要调用用户自定义函数!
    再次调用同一函数
    

    参数传递

    在 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() 函数来查看内存地址变化:

    def change(a):
        print(id(a))   # 指向的是同一个对象
        a=10
        print(id(a))   # 一个新对象
     
    a=1
    print(id(a))
    change(a)
    
    4379369136
    4379369136
    4379369424
    

    可以看见在调用函数前后,形参和实参指向的是同一个对象(对象 id 相同),在函数内部修改形参后,形参指向的是不同的 id。
    传可变对象实例
    可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了。例如:

    #!/usr/bin/python3
     
    # 可写函数说明
    def changeme( mylist ):
       "修改传入的列表"
       mylist.append([1,2,3,4])
       print ("函数内取值: ", mylist)
       return
     
    # 调用changeme函数
    mylist = [10,20,30]
    changeme( mylist )
    print ("函数外取值: ", mylist)
    
    函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
    函数外取值:  [10, 20, 30, [1, 2, 3, 4]]
    

    参数

    以下是调用函数时可使用的正式参数类型:
    必需参数
    关键字参数
    默认参数
    不定长参数
    必需参数

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

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

    #!/usr/bin/python3
     
    #可写函数说明
    def printme( str ):
       "打印任何传入的字符串"
       print (str)
       return
     
    # 调用 printme 函数,不加参数会报错
    printme()
    
    Traceback (most recent call last):
      File "test.py", line 10, in <module>
        printme()
    TypeError: printme() missing 1 required positional argument: 'str'
    

    关键字参数

    关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
    使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
    以下实例在函数 printme() 调用时使用参数名:

    #!/usr/bin/python3
     
    #可写函数说明
    def printme( str ):
       "打印任何传入的字符串"
       print (str)
       return
     
    #调用printme函数
    printme( str = "hello python")
    
    hello python
    

    函数参数的使用不需要使用指定顺序:
    与java不同,java顺序不对的话,如果类型不一致会报错的,即使类型一致结果也是不对的

    #!/usr/bin/python3
     
    #可写函数说明
    def printinfo( name, age ):
       "打印任何传入的字符串"
       print ("名字: ", name)
       print ("年龄: ", age)
       return
     
    #调用printinfo函数
    printinfo( age=50, name="runoob" )
    
    名字:  runoob
    年龄:  50
    

    默认参数

    调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则使用默认值:

    #!/usr/bin/python3
     
    #可写函数说明
    def printinfo( name, age = 35 ):
       "打印任何传入的字符串"
       print ("名字: ", name)
       print ("年龄: ", age)
       return
     
    #调用printinfo函数
    printinfo( age=50, name="runoob" )
    print ("------------------------")
    printinfo( name="runoob" )
    
    名字:  runoob
    年龄:  50
    ------------------------
    名字:  runoob
    年龄:  35
    

    不定长参数

    你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名。基本语法如下:

    def functionname([formal_args,] *var_args_tuple ):
       "函数_文档字符串"
       function_suite
       return [expression]
    

    加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。

    #!/usr/bin/python3
      
    # 可写函数说明
    def printinfo( arg1, *vartuple ):
       "打印任何传入的参数"
       print ("输出: ")
       print (arg1)
       print (vartuple)
     
    # 调用printinfo 函数
    printinfo( 70, 60, 50 )
    
    输出: 
    70
    (60, 50)
    

    如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。如下实例:

    #!/usr/bin/python3
     
    # 可写函数说明
    def printinfo( arg1, *vartuple ):
       "打印任何传入的参数"
       print ("输出: ")
       print (arg1)
       for var in vartuple:
          print (var)
       return
     
    # 调用printinfo 函数
    printinfo( 10 )
    printinfo( 70, 60, 50 )
    
    输出:
    10
    输出:
    70
    60
    50
    

    还有一种就是参数带两个星号 **基本语法如下:

    def functionname([formal_args,] **var_args_dict ):
       "函数_文档字符串"
       function_suite
       return [expression]
    
    #!/usr/bin/python3
      
    # 可写函数说明
    def printinfo( arg1, **vardict ):
       "打印任何传入的参数"
       print ("输出: ")
       print (arg1)
       print (vardict)
     
    # 调用printinfo 函数
    printinfo(1, a=2,b=3)
    
    输出: 
    1
    {'a': 2, 'b': 3}
    

    声明函数时,参数中星号 * 可以单独出现,例如:

    def f(a,b,*,c):
        return a+b+c
    

    如果单独出现星号 *,则星号 * 后的参数必须用关键字传入:

    >>> def f(a,b,*,c):
    ...     return a+b+c
    ... 
    >>> f(1,2,3)   # 报错
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: f() takes 2 positional arguments but 3 were given
    >>> f(1,2,c=3) # 正常
    6
    >>>
    

    匿名函数

    Python 使用 lambda 来创建匿名函数。
    所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
    lambda 只是一个表达式,函数体比 def 简单很多。
    lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
    lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
    虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
    语法
    lambda 函数的语法只包含一个语句,如下:

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

    设置参数 a 加上 10:

    x = lambda a : a + 10
    print(x(5))
    
    15
    

    以下实例匿名函数设置两个参数:

    #!/usr/bin/python3
     
    # 可写函数说明
    sum = lambda arg1, arg2: arg1 + arg2
     
    # 调用sum函数
    print ("相加后的值为 : ", sum( 10, 20 ))
    print ("相加后的值为 : ", sum( 20, 20 ))
    
    相加后的值为 :  30
    相加后的值为 :  40
    

    我们可以将匿名函数封装在一个函数内,这样可以使用同样的代码来创建多个匿名函数。
    以下实例将匿名函数封装在 myfunc 函数中,通过传入不同的参数来创建不同的匿名函数:

    def myfunc(n):
      return lambda a : a * n
     
    mydoubler = myfunc(2)
    mytripler = myfunc(3)
     
    print(mydoubler(11))
    print(mytripler(11))
    
    22
    33
    

    return 语句

    return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的 return 语句返回 None。之前的例子都没有示范如何返回数值,以下实例演示了 return 语句的用法:

    #!/usr/bin/python3
     
    # 可写函数说明
    def sum( arg1, arg2 ):
       # 返回2个参数的和."
       total = arg1 + arg2
       print ("函数内 : ", total)
       return total
     
    # 调用sum函数
    total = sum( 10, 20 )
    print ("函数外 : ", total)
    
    函数内 :  30
    函数外 :  30
    

    强制位置参数

    Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。
    在以下的例子中,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参:

    def f(a, b, /, c, d, *, e, f):
        print(a, b, c, d, e, f)
    

    以下使用方法是正确的:

    f(10, 20, 30, d=40, e=50, f=60)
    

    以下使用方法会发生错误:

    f(10, b=20, c=30, d=40, e=50, f=60)   # b 不能使用关键字参数的形式
    f(10, 20, 30, 40, 50, f=60)           # e 必须使用关键字参数的形式
    

    Python3 数据结构

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

    >>> a = [66.25, 333, 333, 1, 1234.5]
    >>> print(a.count(333), a.count(66.25), a.count('x'))
    2 1 0
    >>> a.insert(2, -1)
    >>> a.append(333)
    >>> a
    [66.25, 333, -1, 333, 1, 1234.5, 333]
    >>> a.index(333)
    1
    >>> a.remove(333)
    >>> a
    [66.25, -1, 333, 1, 1234.5, 333]
    >>> a.reverse()
    >>> a
    [333, 1234.5, 1, 333, -1, 66.25]
    >>> a.sort()
    >>> a
    [-1, 1, 66.25, 333, 333, 1234.5]
    

    将列表当做堆栈使用

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

    >>> stack = [3, 4, 5]
    >>> stack.append(6)
    >>> stack.append(7)
    >>> stack
    [3, 4, 5, 6, 7]
    >>> stack.pop()
    7
    >>> stack
    [3, 4, 5, 6]
    >>> stack.pop()
    6
    >>> stack.pop()
    5
    >>> stack
    [3, 4]
    

    将列表当作队列使用

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

    >>> from collections import deque
    >>> queue = deque(["Eric", "John", "Michael"])
    >>> queue.append("Terry")           # Terry arrives
    >>> queue.append("Graham")          # Graham arrives
    >>> queue.popleft()                 # The first to arrive now leaves
    'Eric'
    >>> queue.popleft()                 # The second to arrive now leaves
    'John'
    >>> queue                           # Remaining queue in order of arrival
    deque(['Michael', 'Terry', 'Graham'])
    

    元组和序列

    元组由若干逗号分隔的值组成,例如:

    >>> t = 12345, 54321, 'hello!'
    >>> t[0]
    12345
    >>> t
    (12345, 54321, 'hello!')
    >>> # Tuples may be nested:
    ... u = t, (1, 2, 3, 4, 5)
    >>> u
    ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
    

    集合

    集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。
    可以用大括号({})创建集合。注意:如果要创建一个空集合,你必须用 set() 而不是 {} ;后者创建一个空的字典,下一节我们会介绍这个数据结构。
    以下是一个简单的演示:

    >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
    >>> print(basket)                      # 删除重复的
    {'orange', 'banana', 'pear', 'apple'}
    >>> 'orange' in basket                 # 检测成员
    True
    >>> 'crabgrass' in basket
    False
    
    >>> # 以下演示了两个集合的操作
    ...
    >>> a = set('abracadabra')
    >>> b = set('alacazam')
    >>> a                                  # a 中唯一的字母
    {'a', 'r', 'b', 'c', 'd'}
    >>> a - b                              # 在 a 中的字母,但不在 b 中
    {'r', 'd', 'b'}
    >>> a | b                              # 在 a 或 b 中的字母
    {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
    >>> a & b                              # 在 a 和 b 中都有的字母
    {'a', 'c'}
    >>> a ^ b                              # 在 a 或 b 中的字母,但不同时在 a 和 b 中
    {'r', 'd', 'b', 'm', 'z', 'l'}
    
    >>> a = {x for x in 'abracadabra' if x not in 'abc'}
    >>> a
    {'r', 'd'}
    

    字典

    另一个非常有用的 Python 内建数据类型是字典。
    序列是以连续的整数为索引,与此不同的是,字典以关键字为索引,关键字可以是任意不可变类型,通常用字符串或数值。
    理解字典的最佳方式是把它看做无序的键=>值对集合。在同一个字典之内,关键字必须是互不相同。
    一对大括号创建一个空的字典:{}。
    这是一个字典运用的简单例子:

    >>> tel = {'jack': 4098, 'sape': 4139}
    >>> tel['guido'] = 4127
    >>> tel
    {'sape': 4139, 'guido': 4127, 'jack': 4098}
    >>> tel['jack']
    4098
    >>> del tel['sape']
    >>> tel['irv'] = 4127
    >>> tel
    {'guido': 4127, 'irv': 4127, 'jack': 4098}
    >>> list(tel.keys())
    ['irv', 'guido', 'jack']
    >>> sorted(tel.keys())
    ['guido', 'irv', 'jack']
    >>> 'guido' in tel
    True
    >>> 'jack' not in tel
    False
    

    构造函数 dict() 直接从键值对元组列表中构建字典。如果有固定的模式,列表推导式指定特定的键值对:

    >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
    {'sape': 4139, 'jack': 4098, 'guido': 4127}
    

    遍历技巧

    在字典中遍历时,关键字和对应的值可以使用 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
    

    Python3 模块

    如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。
    为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。
    模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
    下面是一个使用 python 标准库中模块的例子。

    #!/usr/bin/python3
    # 文件名: using_sys.py
     
    import sys
     
    print('命令行参数如下:')
    for i in sys.argv:
       print(i)
     
    print('\n\nPython 路径为:', sys.path, '\n')
    
    $ python using_sys.py 参数1 参数2
    命令行参数如下:
    using_sys.py
    参数1
    参数2
    
    
    Python 路径为: ['/root', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages'] 
    

    1、import sys 引入 python 标准库中的 sys.py 模块;这是引入某一模块的方法。
    2、sys.argv 是一个包含命令行参数的列表。
    3、sys.path 包含了一个 Python 解释器自动查找所需模块的路径的列表。

    import 语句

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

    import module1[, module2[,... moduleN]
    
    #!/usr/bin/python3
    # Filename: support.py
     
    def print_func( par ):
        print ("Hello : ", par)
        return
    
    #!/usr/bin/python3
    # Filename: test.py
     
    # 导入模块
    import support
     
    # 现在可以调用模块里包含的函数了
    support.print_func("Runoob")
    
    $ python3 test.py 
    Hello :  Runoob
    

    一个模块只会被导入一次,不管你执行了多少次 import。这样可以防止导入模块被一遍又一遍地执行。
    当我们使用 import 语句的时候,Python 解释器是怎样找到对应的文件的呢?
    这就涉及到 Python 的搜索路径,搜索路径是由一系列目录名组成的,Python 解释器就依次从这些目录中去寻找所引入的模块。
    这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。
    搜索路径是在 Python 编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在 sys 模块中的 path 变量,做一个简单的实验,在交互式解释器中,输入以下代码:

    >>> import sys
    >>> sys.path
    ['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
    >>> 
    

    from … import 语句

    Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下:

    from modname import name1[, name2[, ... nameN]]
    
    >>> from fibo import fib, fib2
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

    这个声明不会把整个fibo模块导入到当前的命名空间中,它只会将fibo里的fib函数引入进来。

    from … import * 语句

    把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

    from modname import *
    

    这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

    深入模块

    模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
    每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。
    所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞混。
    从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。
    模块是可以导入其他模块的。在一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,当然这只是一个惯例,而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。

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

    >>> from fibo import fib, fib2
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

    这种导入的方法不会把被导入的模块的名称放在当前的字符表中(所以在这个例子里面,fibo 这个名称是没有定义的)。
    这还有一种方法,可以一次性的把模块中的所有(函数,变量)名称都导入到当前模块的字符表:

    >>> from fibo import *
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

    这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。

    name属性

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

    #!/usr/bin/python3
    # Filename: using_name.py
    
    if __name__ == '__main__':
       print('程序自身在运行')
    else:
       print('我来自另一模块')
    
    $ python using_name.py
    程序自身在运行
    
    $ python
    >>> import using_name
    我来自另一模块
    >>>
    

    说明: 每个模块都有一个name属性,当其值是'main'时,表明该模块自身在运行,否则是被引入。
    说明:namemain 底下是双下划线, _ _ 是这样去掉中间的那个空格。

    dir() 函数

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

    >>> import fibo, sys
    >>> dir(fibo)
    ['__name__', 'fib', 'fib2']
    >>> dir(sys)  
    ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
     '__package__', '__stderr__', '__stdin__', '__stdout__',
     '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
     '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
     'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
     'call_tracing', 'callstats', 'copyright', 'displayhook',
     'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
     'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
     'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
     'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
     'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
     'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
     'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
     'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
     'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
     'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
     'thread_info', 'version', 'version_info', 'warnoptions']
    

    如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:

    >>> a = [1, 2, 3, 4, 5]
    >>> import fibo
    >>> fib = fibo.fib
    >>> dir() # 得到一个当前模块中定义的属性列表
    ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
    >>> a = 5 # 建立一个新的变量 'a'
    >>> dir()
    ['__builtins__', '__doc__', '__name__', 'a', 'sys']
    >>>
    >>> del a # 删除变量名a
    >>>
    >>> dir()
    ['__builtins__', '__doc__', '__name__', 'sys']
    >>>
    

    标准模块

    Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。
    有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。
    这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。
    应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:

    >>> import sys
    >>> sys.ps1
    '>>> '
    >>> sys.ps2
    '... '
    >>> sys.ps1 = 'C> '
    C> print('Runoob!')
    Runoob!
    C> 
    

    包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。
    比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。
    就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。
    这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。
    不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。
    现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。
    并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。
    这里给出了一种可能的包结构(在分层的文件系统中):

    sound/                          顶层包
          __init__.py               初始化 sound 包
          formats/                  文件格式转换子包
                  __init__.py
                  wavread.py
                  wavwrite.py
                  aiffread.py
                  aiffwrite.py
                  auread.py
                  auwrite.py
                  ...
          effects/                  声音效果子包
                  __init__.py
                  echo.py
                  surround.py
                  reverse.py
                  ...
          filters/                  filters 子包
                  __init__.py
                  equalizer.py
                  vocoder.py
                  karaoke.py
                  ...
    

    在导入一个包的时候,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被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。
    这个功能并不常用,一般用来扩展包里面的模块。

    个人理解就是相当于java中的导包,如果你需要其他类的方法就需要导入类去调用相应的方法。比如:工具类中的一些方法。

    相关文章

      网友评论

          本文标题:2023-02-01 Python 3基础四

          本文链接:https://www.haomeiwen.com/subject/qusmhdtx.html