[Python基础]二、函数、模块、异常、面向对象、作用域

[Python基础]函数、模块、异常、面向对象、作用域

一、Python函数

调用函数时可使用的正式参数类型
  • 必需参数:需要以正确的顺序传入函数。调用时的数量必须和声明时的一样。
  • 关键词参数:解释器能够用参数名匹配参数值。
  • 默认参数:调用函数时,如果没有传递参数,则会使用默认参数。默认参数必须放在最后面。
  • 不可变参数:函数能处理比声明时更多的参数。声明时不会命名。
    • 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
    • 加了两个星号 ** 的参数会以字典的形式导入。
    • 如果单独出现星号 * 后的参数必须用关键字传入。
lamba匿名函数

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

二、Python模块

from modname import name1[, name2[, ... nameN]] 从模块中导入一个指定的部分到当前命名空间中
from modname import * 把一个模块的所有内容全都导入到当前的命名空间
一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。
内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回。

包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。一个模块的名称是 A.B, 那么他表示一个包A中的子模块B 。
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包。

  1. 用户可以每次只导入一个包里面的特定模块,比如import sound.effects.echo,这将会导入子模块:sound.effects.echo。他必须使用全名去访问:sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
  2. 还有一种导入子模块的方法是:from sound.effects import echo,这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:echo.echofilter(input, output, delay=0.7, atten=4)
  3. 还有一种变化就是直接导入一个函数或者变量:from sound.effects.echo import echofilter,同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:echofilter(input, output, delay=0.7, atten=4)
  4. 注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。

导入语句遵循如下规则:如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,得在其他`init.p被执行前定义。

三、Python错误与异常

assert(断言)

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况。

语法格式如下:

assert expression

等价于:

if not expression:
    raise AssertionError

assert 后面也可以紧跟参数:

assert expression [, arguments]

等价于:

if not expression:
    raise AssertionError(arguments)
#判断当前系统是否为 Linux,如果不满足条件则直接触发异常,不必执行接下来的代码
import sys
assert ('linux' in sys.platform), "该代码只能在 Linux 下执行"
try/except

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

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

一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。
一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。
最后一个except子句可以忽略异常的名称,它将被当作通配符使用。可以使用它打印一个错误信息,然后再次把异常抛出。

import sys
try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except (RuntimeError, TypeError, NameError):
    pass
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

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

>>>def this_fails():
        x = 1/0   
>>> try:
        this_fails()
    except ZeroDivisionError as err:
        print('Handling run-time error:', err)   
Handling run-time error: int division or modulo by zero
# 处理带有参数的异常
def temp_convert(var):
    try:
        return int(var)
    except (ValueError) as Argument:
        print ("参数没有包含数字\n", Argument)
temp_convert("xyz");
try/except…else、try-finally

如果使用else子句,那么必须放在所有的 except 子句之后,else 子句将在 try 子句没有发生任何异常的时候执行。
使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。
try-finally 语句无论是否发生异常都将执行最后的代码。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()
抛出异常

raise [Exception [, args [, traceback]]]:使用 raise 语句抛出一个指定的异常。
raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。
如果一个异常在 try 子句里(或者在 except 和 else 子句里)被抛出,而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后被抛出。

>>>def divide(x, y):
        try:
            result = x / y
        except ZeroDivisionError:
            print("division by zero!")
        else:
            print("result is", result)
        finally:
            print("executing finally clause")
   
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
Python3 内置异常类型的结构:
BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

四、Python3 面向对象

init()与self

类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用。
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 selfself 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。self 不是 python 关键字。

class Test:
    def prt(self):
        print(self)
        print(self.__class__)
t = Test()
t.prt()
#<__main__.Test instance at 0x100771878>
#__main__.Test
继承
#Python支持多继承形式。
class DerivedClassName(BaseClassName1[, Base2, Base3...)]):
    <statement-1>
    ...
    <statement-N>

若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索。

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

  1. 子类需要自动调用父类的方法:子类不重写__init__()方法,实例化子类后,会自动调用父类的__init__()的方法。
  2. 子类不需要自动调用父类的方法:子类重写__init__()方法,实例化子类后,将不会自动调用父类的__init__()的方法。
  3. 子类重写__init__()方法又要继承父类的构造方法,可以使用 super 关键字:
    • 写法一:super(子类,self).__init__(参数1,参数2,....)
    • 写法二: 父类名称.__init__(self,参数1,参数2,...)
    class Son(Father):  #写法二
      def __init__(self, name):   
        super(Son, self).__init__(name)
    
类属性与方法

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

#类的私有属性实例
#!/usr/bin/python3
class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount) 
counter = JustCounter()
counter.count()  #1
print (counter.publicCount)  #1
print (counter.__secretCount)  # 报错,实例不能访问私有变量

类的方法:使用 def 关键字来定义一个方法,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。self 的名字并不是规定死的。
类的私有方法__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods

#类的私有方法实例
#!/usr/bin/python3 
class Site:
    def __init__(self, name, url):
        self.name = name       # public
        self.__url = url   # private 
    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url) 
    def __foo(self):          # 私有方法
        print('这是私有方法') 
    def foo(self):            # 公共方法
        print('这是公共方法')
        self.__foo() 
x = Site('菜鸟教程', 'www.runoob.com')
x.who()        # 正常输出
x.foo()        # 正常输出
x.__foo()      # 报错

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

五、命名空间与作用域

命名空间查找顺序:局部的命名空间去 -> 全局命名空间 -> 内置命名空间。
命名空间的生命周期:命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。因此,我们无法从外部命名空间访问内部命名空间的对象。
在一个 python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/try/exceptfor/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问
有四种作用域,规则顺序: L –> E –> G –> B
L(Local):最内层,包含局部变量,比如一个函数/方法内部。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
G(Global):当前脚本的最外层,比如当前模块的全局变量。
B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

global 和 nonlocal关键字
num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) #1
    num = 123
    print(num) #123
fun1()
print(num) #123

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

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)  #100
    inner()
    print(num) #100
outer()

有一种特殊情况,假设下面这段代码被运行:

>>> a = 10
>>> def test():
        a = a + 1
        print(a)
>>> test()
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 5, in test
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。修改 a 为全局变量,通过函数参数传递,可以正常执行输出结果

发布了54 篇原创文章 · 获赞 3 · 访问量 3654

猜你喜欢

转载自blog.csdn.net/magic_jiayu/article/details/104104515