[python] Class

Fisrt Look

使用C++术语, Python 类的所有成员(包括函数和数据)均为 "public", 所有函数均为"virtual"
支持多继承
支持操作符重载
内建类型可用作基类

关于global, nonlocal 的区别,Python documentation 给出的例子:

def scope_test():
    def do_local():
        spam = "local spam"  # local变量
    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"
    def do_global():
        global spam
        spam = "global spam"
    spam = "test spam"  # 此变量,非global
    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

关于global, nonlocal 的区别, Stackoverflow给出的例子:
不使用 nonlocal关键字:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 1
# global: 0

使用nonlocal关键字:

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 2
# global: 0

而如果使用 global, 则x会绑定到global值:

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)
# inner: 2
# outer: 1
# global: 2

简短地说,使用 nonlocal关键字可以将值赋给外层(是outer space, 是”closest”的那个,不是global!)的变量。通过例子,能很容易看出两个关键字之间的区别。相比之下,global会很高兴地忽略所有其他变量,只取最外层的那个。


定义类的语法

最简形式:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

类对象(Class Objects)

Python中,class也为对象。
类对象支持两种形式的操作:属性引用、实例化。
假如一个类定义如下:

class MyClass:
    """A simple example class"""
    i = 12345
    def f(self):
        return 'hello world'

self 参数

则可以通过类名引用属性:MyClass.iMyClass.fMyClass.__doc__, 他们一个返回整数,一个返回函数对象,最后一个返回类的docstring,即字符串“A simple example class”。可以通过赋值修改属性。
类的实例化使用函数表示法:

x = MyClass()

但是用这种方法创建的实例对象是一个空对象,为了将实例初始化为自定义的状态,class 必须添加名为__init__()的特殊方法:

def __init__(self):
    self.data = []

之后,实例化类时,这个方法将被自动调用初始化实例。


实例对象

可以通过属性引用操纵实例,实例有两种属性: 数据属性,方法属性。
数据属性不需要事先声明,赋值就存在,例如,下面的代码输出16,但不会留下任何痕迹:

x.counter = 1 # 赋值后立即存在
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)
del x.counter # 删除

方法对象 (Method Objects)

Method对象可以先储存起来,然在需要的时候再调用,例如如下的代码:

xf = x.f
while True:
    print(xf())

类和实例变量

某些数据类型如list不应当用作类变量, 因为这样的变量将被所有的类实例共享:

class Dog:
    tricks = []                 # Error: 错误地使用了类的变量
    def __init__(self, name):
        self.name = name
    def add_trick(self, trick):
        self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                    #意料之外,此变量为所有对象共享
['roll over', 'play dead']

而应当改成实例变量

class Dog:
    def __init__(self, name):
        self.name = name
        self.tricks = []    # creates a new empty list for each dog
    def add_trick(self, trick):
        self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

对于类来说,属性分两种: 数据属性(数据成员), 方法属性(成员函数内部定义的变量),如果两种变量同名,数据属性将覆盖方法属性:

class MyClass:
    attr = 111           # 为避免名字冲突,一般改为 _attr = 111
    def func(self):
        attr = 222       # 方法属性(method attribute)
if __name__ == "__main__":
    obj = MyClass()
    print(obj.attr)

# 111

为了避免名字冲突,一般在数据属性前面加下划线。
方法可以定义在内的外部,方法可以调用其他方法:

def add(self, x):         # 方法定义在类的外部
    self.data.append(x)
class Bag:
    f = add
    def __init__(self):
        self.data = []
    def addtwice(self, x):  # 方法调用方法
        self.f(x)
        self.f(x)
b = Bag()
b.addtwice('dog ')
print(b.data)
# ['dog ', 'dog ']

继承

派生类的一般定义形式:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

派生类可以重写基类的方法。

多重继承

多继承类的一般定义形式:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

私有变量

虽然Python类不存在“私有”变量,但是绝大多数代码都遵从这样的约定:以下划线开头的名字被看作非公开的,例如 _spam


生成器

语法定义类似于下面:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index] # yield 相当于return,下次执行,会从此处接着执行。

>>> for char in reverse('golf'):
...     print(char)
...
f
l
o
g

生成器表达式

写法极为精简,但是不知道这个和类的概念有什么关系。

>>> sum(i*i for i in range(10))                 # sum of squares
285
>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260
>>> from math import pi, sin
>>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)}
>>> unique_words = set(word  for line in page  for word in line.split())
>>> valedictorian = max((student.gpa, student.name) for student in graduates) # 不确定含义
>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']


[1] Python 3.4.4 documentation: classes
[2] https://stackoverflow.com/questions/1261875/python-nonlocal-statement

猜你喜欢

转载自blog.csdn.net/ftell/article/details/80336522