
域和作用空间
本地域,函数域(nonlocal)和 全局域(global)
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam" spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam) scope_test()
print("In global scope:", spam)
输出的结果是
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
* 简要解释一下:
本地域作用于当前子函数范围,函数域作用于整个函数范围,全局域作用于函数以及函数外部。优先级是本地域>函数域>全局域。
类的基本概念
最简单的类的定义形式看起来像这样:你可以将一个类定义放置于 if
语句的分支中, 或一个函数中.
class ClassName:
<statement-1>
.
.
.
<statement-N>
类的属性和类的初始化方法
比如:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
可以给 MyClass.i
赋值以改变其数值. __doc__
也是一个合法的属性,返回属于这个类的 docstring : "A simple example class"
.
实例化的操作 (“调用” 一个类对象) 创建了空的对象. 在创建实例时, 很多类可能都需要有特定的初始状态. 所以一个类可以定义一个特殊的方法, 称为 __init__()
, 像这样:
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
实例对象
可以对实例对象做什么? 唯一能理解的操作就是属性引用. 有两种合法的属性, 数据属性和方法.(在 Python 中, 方法的概念并不是类实例所特有: 其他对象类型也可以有方法. 例如, 列表对象有 append, insert, remove, sort, 及等等的方法. 但是, 在下面的讨论中, 我们指的就是类实例对象的方法, 除非特别指出.)(对象不等于类)
方法对象
调用方法对象
x.f()
在多数情况下, 调用一个方法 (有个 n 个参数), 和调用相应的函数 (也有那 n 个参数, 但是再额外加入一个使用该方法的对象), 是等价的.
当一个实例属性被引用时, 但是不是数据属性, 那么它的类将被搜索. 如果该名字代表一个合法的类属性并且是一个函数对象, 一个方法对象就会被创建, 通过包装 (指向) 实例对象, 而函数对象仍然只是在抽象的对象中: 这就是方法对象. 当方法对象用一个参数列表调用, 新的参数列表会从实例对象中重新构建, 然后函数对象则调用新的参数列表.
注意:
数据属性覆写了同名的方法属性; 为了避免这个偶然的名字冲突, 在大型的程序中这会导致很难寻找的 bug, 使用某些命名约定是非常明智的, 这样可以最小的避免冲突. 可能的约定包括大写方法名称, 在数据类型前增加特殊的前缀 (或者就是一个下划线), 或对于方法使用动词, 而数据成员则使用名词.
继承
派生类的定义:
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
BaseClassName
的定义对于派生类而言必须是可见的. 在基类的地方, 任意的表达式都是允许的. 这就会非常有用, 比如基类定义在另一个模块:
class DerivedClassName(modname.BaseClassName):
Python 有两个内置函数用于继承:
- 使用
isinstance()
检查实例的类型:isinstance(obj, int)
只有在obj.__class__
是int
或其派生类时才为True
. - 使用
issubclass()
用于检查类的继承关系:issubclass(bool, int)
会返回True
, 因为bool
是int
的派生类. 但是,issubclass(float, int)
会是False
因为float
并不是int
的派生类.
多重继承
Python 支持多重继承.
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
在继承体系中, 同样的类只会被搜寻一次. 如果一个属性在 DerivedClassName
中没有被找到, 它就会搜寻 Base1, 然后 (递归地) 搜寻 Base1
的基类, 然后如果还是没有找到, 那么就会搜索 Base2
, 等等.
私有变量
在 Python 之中, 并不存在那种无法访问的 “私有” 变量. 但是, 在多数的 Python 代码中有个约定: 以一个下划线带头的名字 (如 _spam
) 应该作为非公共的 API (不管是函数, 方法或者数据成员). 这应该作为具体的实现, 而且变化它也无须提醒.
有这样的一种机制称为 name mangling. 任何如 __spam
形式的标识符, (在开头至少有两个下划线) 将被替换为 _classname__spam
, 此处的 classname
就是当前的类. 这样的处理无须关注标识符的句法上的位置, 尽管它是在一个类的定义中.
数据类型
绑定一些命名的数据. 一个空的类定义就将很好:
class Employee:
pass john = Employee() # Create an empty employee record # Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
一段 Python 代码中如果希望一个抽象的数据类型, 那么可以通过传递一个类给那个方法, 就好像有了那个数据类型一样.