[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
的文件才会被认作是一个包。
- 用户可以每次只导入一个包里面的特定模块,比如
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
这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。
导入语句遵循如下规则:如果包定义文件 __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
语句按照如下方式工作;
- 首先,执行
try
子句(在关键字try
和关键字except
之间的语句)。 - 如果没有异常发生,忽略
except
子句,try
子句执行后结束。 - 如果在执行
try
子句的过程中发生了异常,那么try
子句余下的部分将被忽略。如果异常的类型和except
之后的名称相符,那么对应的except
子句将被执行。 - 如果一个异常没有与任何的
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__()
的特殊方法(构造方法),该方法在类实例化时会自动调用。
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self
。self
代表的是类的实例,代表当前对象的地址,而 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 子类继承父类构造函数说明
- 子类需要自动调用父类的方法:子类不重写
__init__()
方法,实例化子类后,会自动调用父类的__init__()
的方法。 - 子类不需要自动调用父类的方法:子类重写
__init__()
方法,实例化子类后,将不会自动调用父类的__init__()
的方法。 - 子类重写
__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/except
、for/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 为全局变量,通过函数参数传递,可以正常执行输出结果