首先申明下,本文为笔者学习《Python学习手册》的笔记,并加入笔者自己的理解和归纳总结。

1. 类的定义

class语句创建类对象并将其赋值给变量名,类可以看成是模块中的变量。

>>> class Sample:
    def setValue(self, val):          # self指向类实例
        self.val = val                # 添加一个实例变量val
    def getValue(self):               # 返回实例变量val的值
        return self.val

2. 创建实例

每个实例都拥有自己的命名空间,实例变量的修改不会影响其他实例。

>>> s = Sample()                      # 在Sample加上括号创建类实例
>>> s.setValue("Hello World!")        # 调用方法setValue
>>> s.getValue()
'Hello World!'

>>> s1 = Sample()                     # 创建另一个实例s1
>>> s1.setValue("Welcome")            # 在s1上调用方法
>>> s1.getValue()
'Welcome'

>>> s.getValue()                      # s和s1互不干扰
'Hello World!'

3. 访问变量和方法

通过逗号(.)访问变量和方法

>>> s = Sample()
>>> s.val = "Welcome"                 # 访问实例变量
>>> s.getValue()
'Welcome'

通过__dict__变量访问变量

>>> s.__dict__                        # __dict__是一个字典
{'val': 'Welcome'}

>>> s.__dict__.get("val")
'Welcome'

通过类来访问方法

>>> Sample.setValue(s, "value init")  # 类似于s.setValue("value init")
>>> s.getValue()
'value init'

4. 构造函数

新的实例构造时,会调用__init__()方法。

>>> class Shape:
    def __init__(self, x, y, w=10, h=10):
        self.x = x
        self.y = y
        self.width = w
        self.height = h
    def draw(self):
        print "draw"

访问构造函数,构造函数同一般的函数一样,可以设默认值,通过关键字访问。

>>> s = Shape(4, 5)                 # width和height采用默认值
>>> s.__dict__
{'y': 5, 'x': 4, 'height': 10, 'width': 10}

>>> s = Shape(4, 5, 100)
>>> s.__dict__
{'y': 5, 'x': 4, 'height': 10, 'width': 100}

>>> s = Shape(4, 5, w=50, h=45)     # 采用混合参数
>>> s.__dict__
{'y': 5, 'x': 4, 'height': 45, 'width': 50}

对于一个已经定义了构造函数的类来说,默认构造方法不一定有效。

>>> s = Shape()
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    s = Shape()
TypeError: __init__() takes at least 3 arguments (1 given)

5. 类属性

__dict__显示类的属性。

>>> s = Shape(4, 5, 50, 45)
>>> s.__dict__
{'y': 5, 'x': 4, 'height': 45, 'width': 50}

>>> Shape.__dict__                  # 类显示属性和方法
{'__module__': '__main__', 'draw': <function draw at 0x0000000002BF1208>, '__ini
t__': <function __init__ at 0x0000000002BF1198>, '__doc__': None}

__bases__显示父类

>>> Shape.__bases__
()

>>> class Circle(Shape):            # Cirle继承Shape类
    pass
>>> Circle.__bases__
(<class __main__.Shape at 0x0000000002BED708>,)

>>> class Oval(Shape, Circle):      # Oval继承两个类
    pass
>>> Oval.__bases__
(<class __main__.Shape at 0x0000000002BED708>, <class __main__.Circle at 0x0000000002BED768>)

6. 类变量

Shape中有一个变量color,作为Shape的类变量。

>>> Shape.color = "red"
>>> s1 = Shape(3, 4)                # 创建Shape实例s1
>>> s2 = Shape(5, 6)                # 创建Shape实例s2
>>> Shape.color, s1.color, s2.color # 所有实例的color都是一样的
('red', 'red', 'red')

修改这个类变量,会影响其他实例。

>>> Shape.color = "blue"            # 修改类变量后,所有的实例都被修改
>>> Shape.color, s1.color, s2.color
('blue', 'blue', 'blue')

如果在某个实例中修改了这个变量,实际会把这个变量本地化,二期不会影响其他实例。

>>> s1.color = "green"              # 实例设置color值,color成为实例的本地变量
>>> Shape.color, s1.color, s2.color
('blue', 'green', 'blue')

>>> Shape.color = "red"             # 再次修改Shape类的color变量
>>> Shape.color, s1.color, s2.color # s1已经设置了本地变量,不会受到影响
('red', 'green', 'red')

可以设置方法。

>>> def draw(self):
    print "position at (%d, %d)" % (self.x, self.y)
>>> Shape.draw = draw               # 把方法赋值给一个变量
>>> Shape.draw(s1)
position at (3, 4)                  # Shape类调用方法

>>> s1.draw()                       # s1实例调用方法
position at (3, 4)

7. 方法重载

两个相同名称的方法,最后出现的方法会覆盖之前所有的方法,类似于给一个变量赋值,只有最后一次有效

>>> class Sample:
    def add(self, val1, val2):
        return val1 + val2
    def add(self, val1, val2, val3):
        return val1 + val2 + val3

>>> s = Sample()
>>> s.add(13, 23)                   # 调用add方法,add方法被覆盖
Traceback (most recent call last):
  File "<pyshell#71>", line 1, in <module>
    s.add(13, 23)
TypeError: add() takes exactly 4 arguments (3 given)

>>> s.add(13, 23, 37)
73

8. 类的继承

8.1 继承

实现继承需要在类名后面的括号内加入需要继承的类,Circle类继承了Shape类,同时继承了变量、构造函数和其他方法。

>>> class Shape:
    def __init__(self, x, y, w=10, h=10):		
        self.x = x
        self.y = y
        self.width = w
        self.height = h
    def draw(self):
        print "Shape draw"

>>> class Circle(Shape):
    pass

调用Circle

>>> c = Circle(50, 65)               # Circle继承了Shape的构造函数和属性
>>> c.__dict__
{'y': 65, 'x': 50, 'height': 10, 'width': 10}

>>> c.draw()                         # Circle继承了Shape的draw方法
Shape draw

8.2 方法覆盖。

覆盖Circle的构造函数和draw方法。

>>> class Circle(Shape):
    def __init__(self, x, y, radius=10):
        Shape.__init__(self, x, y, 0, 0)
        self.radius = radius
    def draw(self):
        print "Circle draw"

>>> c = Circle(50, 65)               # 调用Circle的构造函数
>>> c.__dict__
{'y': 65, 'x': 50, 'height': 0, 'radius': 10, 'width': 0}

>>> c = Circle(50, 65, 20)
>>> c.__dict__
{'y': 65, 'x': 50, 'height': 0, 'radius': 20, 'width': 0}

>>> c = Circle(50, 65, 20, 20)
Traceback (most recent call last):
  File "<pyshell#188>", line 1, in <module>
    c = Circle(50, 65, 20, 20)
TypeError: __init__() takes at most 4 arguments (5 given)

>>> c.draw()                         # 调用Shape的draw方法
Circle draw

8.3 方法调用顺序

Sampe有两个父类Sup1Sup2。查找顺序Sample->Sup1->Sup2
Sup1Sup2是根据在Sample继承顺序排列的。

>>> class Sup1:
    def fun(self):
        print "Sup1.fun"
    def fun1(self):
        print "Sup1.fun1"

>>> class Sup2:
    def fun(self):
        print "Sup2.fun"
    def fun1(self):
        print "Sup2.fun1"
    def fun2(self):
        print "Sup2.fun2"

>>> class Sample(Sup1, Sup2):
    def fun(self):
        print "Sample.fun"

>>> s = Sample()
>>> s.fun()                          # 调用Sample自身的fun方法
Sample.fun

>>> s.fun1()                         # 调用Sup1的fun1方法
Sup1.fun1

>>> s.fun2()                         # 调用Sup2的fun2方法
Sup2.fun2

9. 抽象类

>>> from abc import ABCMeta, abstractmethod
>>> class Super:
    __metaclass__ = ABCMeta          # metaclass设置为ABCMeta
    @abstractmethod                  # 方法添加abstractmethod
    def fun(self):
        pass

>>> Super()
Traceback (most recent call last):
  File "<pyshell#46>", line 1, in <module>
    Super()
TypeError: Can't instantiate abstract class Super with abstract methods fun

>>> class Sample(Super):
    def fun(self):
        print "in sample"
>>> Sample().fun()
in sample

10. 特殊变量

>>> class Sample:
    def __init__(self):
        self._x = 12
        self.__x = 13
    def fun(self):
        print "fun"
    def _fun(self):
        print "_fun"
    def __fun(self):
        print "__fun"

>>> s = Sample()
>>> dir(s)                           # __x和__fun变成_Sample__xx
['_Sample__fun', '_Sample__x', '__doc__', '__init__', '__module__', '_fun', '_x', 'fun']

11. 限定属性__slots__

>>> class Sample(object):            # 定义一个继承object的类
    __slots__ = ("name", "age")      # __slots__定义了两个属性

>>> s = Sample()
>>> s.name = "Mike"                  # 可以进行属性name
>>> s.name
'Mike'

>>> s.addr = "ShangHai"              # addr没有定义,无法访问
Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    s.addr = "ShangHai"
AttributeError: 'Sample' object has no attribute 'addr'

12. 自定义类的打印信息

class实例会调用默认打印信息。

>>> class Sample:
    pass
>>> Sample()
<__main__.Sample instance at 0x03302940>

字符串表达式__repr__定义交互式界面的输出。

>>> class Sample:
    def __repr__(self):
        return "[Sample, repr]"

>>> Sample()                        # 交互式界面调用repr方法
[Sample, repr]

>>> print Sample()                  # print方法也会调用repr方法
[Sample, repr]

字符串表达式__str__定义print的输出。

>>> class Sample:
    def __str__(self):
        return "[Sample, str]"

>>> Sample()                        # 交互式界面调用默认方法
<__main__.Sample instance at 0x0000000002AC8848>

>>> print Sample()                  # print方法调用str方法
[Sample, str]

__repr____str__都被定义。

>>> class Sample:
    def __repr__(self):
        return "[Sample, repr]"
    def __str__(self):
        return "[Sample, str]"

>>> Sample()                        # 交互式界面调用repr方法
[Sample, repr]

>>> print Sample()                  # print方法调用str方法
[Sample, str]