python的class与继承

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/local/bin/python3

# -*- coding: utf-8 -*



class Student(object):

    # 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且调用时不传递该参数。除此之外,类的方法和普通函数没有什么区别。
    # 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问 外部不能访问

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s:%s' % (self.__name, self.__score))


bart = Student('梁辉', 100)
bart.print_score()

双下划线开头的实例变量不能直接访问是因为 就__name而言,python解释器对外把它解释为_Student__name,所以仍然可以通过_Student__name访问__name 但是并不建议这么些 因为不同版本的python解释器可能会把__name改成不用的变量名

一、类的继承和多态

判断一个变量是否是某个对象的实例可以用isinstance判断

print(isinstance(bart, Student))

静态语言VS动态语言

对于静态语言(例如java)来说,如果需要传入Animal类型,则传入的对象必须是animal或者他的子类,否则无法调用run()方法

而对于python来说,不一定需要传入animal类型数据 只需要保证传入的对象有一个run()方法就行

1
2
3
4
5
class Timer(object):

     def run(self):

         print('start...')

这就是动态语言的’鸭子类型’,他并不要求严格的继承体系,一个对象只要看起来像鸭子,走起路来像鸭子,那他就可以被看作为鸭子

python的file-like object 就是一种鸭子类型。对真正的文件对象 他有一个read()方法 。但是许多对象 只要有read()方法 都被视为file-like object

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。 动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的

二、使用type()判断对象类型

print(type(bart))

如果需要判断一个对象是否是函数 可以使用types模块中定义的常量 FunctionType、BuiltinFunctionType、LambdaType、GeneratorType

判读一个变量是否是某个类的实例 可以使用isinstance()判断

print(isinstance(bart, Student))

能用type()判断的基本类型也可以用isinstance判断

print(isinstance(‘a’, str))

三、使用dir()

使用dir()函数可以获取对象 的所有属性和方法 他返回一个包含字符串的list

print(dir(bart))

类似__xxx__的属性和方法在py中都是有特殊用途的 比如__len__方法返回长度,py中 使用len()函数获取一个对象的长度 实际上 在len()函数内部 它自动取调用该对象的__len__方法 所以

print(len(‘ABC’))

print(‘ABC’.len())

这两种写法是等价的

除此之外 还可以使用getattr() setattr() 以及 hasattr()

使用get时 如果获取一个不存在的属性 会抛出AttributeError的错误

getattr()第三个参数可以设置默认值

假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。

请注意,在Python这类动态语言中,根据鸭子类型,有read()方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read()方法返回的是有效的图像数据,就不影响读取图像的功能。

四、在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉累的属性,而当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

实例属性属于各个实例所有,互不干扰; 类属性属于类所有,所有实例共享一个属性;

不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

五、使用__slots__

如果我们想要限制实例的属性,比如,只允许对Student实例添加name和age属性

为了达到限制的目的,python允许在定义class 的时候,定义一个特殊的变量__slots__,来限制该class实例能添加的属性

1
2
3
class Person(object):

    __slots__ = ('name', 'age')

slots__仅对当前实例有效 对于继承的子类择不生效 除非在子类中也定义__slots,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

六、使用@property

使用python内置的@property装饰器可以吧一个方法变成一个属性调用

@property的使用把一个getter方法变成一个属性,只需要加上@property就好了,此时@property本身又创建了另一个装饰器,@score.setter,负责把一个setter方法变成属性赋值,于是我们就拥有一个可控的属性操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Car(object):

    @property
    def speed(self):
        return self._speed

    @speed.setter
    def speed(self, value):
        if not isinstance(value, float):
            raise ValueError('speed must be float')
        if value < 0 or value > 255.00:
            raise ValueError('speed must between 0~255')
        self._speed = value

    @property
    def color(self):
        return 'black'


car = Car()
car.speed = 99.00
print(car.speed)
print(car.color)

可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:比如car object中的color属性