–
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。 OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。 class Student(object): 初始化你将要创建的实例的属性 def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score)) 给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出来就像这样: bart = Student('Bart Simpson', 59) lisa = Student('Lisa Simpson', 87) bart.print_score() lisa.print_score() 面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的 一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同 仍以Student类为例,在Python中,定义类是通过class关键字: class Student(object): pass class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念 我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。 class Student(object): def __init__(self, name, score): self.name = name self.score = score 注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。 有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去: 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外, 类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。 既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数, 这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法: class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score)) 要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入: 访问限制 class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print ('%s: %s' %(self.name, self.score)) andy = Student('andy', 59) andy.print_score() andy.score = 60 andy.print_score() 结果: andy: 59 andy: 60 #另外一个例子,变量为私有属性,不能直接在外面修改,无法从外部访问实例变量.__name和实例变量.__score class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print ('%s: %s' %(self.__name, self.__score)) andy = Student('andy', 59) andy.print_score() andy.__score = 60 andy.print_score() 结果: andy: 59 andy: 59 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private), 只有内部可以访问,外部不能访问 在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量, 所以,不能用__name__、__score__这样的变量名 如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法,原先那种直接通过bart.score = 99也可以修改啊,为什么要定义一 个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数: class Student(object): ... def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError('bad score') 继承和多态 当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类 class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): def run(self): print('Dog is running...') def eat(self): print('Dog is eatting...') class Cat(Animal): pass dog = Dog() dog.run() dog.eat() cat = Cat() cat.run() 执行结果 Dog is running... Dog is eatting... Animal is running... 对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。 继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法 当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run(),对方法进行重写 继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写 继承属性示例 class Student(object): def __init__(self, name, score, age): self.name = name self.score = score self.age = age def print_information(self): print('name is %s, score is %s, age is %s'\ %(self.name, self.score, self.age)) class Couple(Student): def __init__(self, name, score, age, sex): Student.__init__(self, name, score, age) self.sex = sex def print_information(self): print('name is %s, score is %s, age is %s, sex is %s'\ %(self.name, self.score, self.age, self.sex)) zhang = Student('zhang', 80, 22) yong = Student('yong', 72, 19) lu = Student('lu', 90, 21) lu.print_information() sui = Couple('sui', 80, 40, 'male') sui.print_information() 多态 多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型, 然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的 run()方法,这就是多态的意思: class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...') def run(abc): abc.run() run(Animal()) run(Dog()) run(Cat()) 获取对象信息 就是判断当前的变量是哪个类型的变量,类是哪个类型的类 对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数 先创建3种类型的对象: >>> a = Animal() >>> d = Dog() >>> h = Husky() 然后,判断: >>> isinstance(h, Husky) True >>> isinstance(h, Dog) True h虽然自身是Husky类型,但由于Husky是从Dog继承下来的,所以,h也还是Dog类型。换句话说,isinstance()判断的是一个对象是否是该类型本身, 或者位于该类型的父继承链上。 >>> isinstance(h, Animal) True >>> isinstance('a', str) True >>> isinstance(123, int) True >>> isinstance(b'a', bytes) True 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法: >>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill'] 配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态: >>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject() 紧接着,可以测试该对象的属性: >>> hasattr(obj, 'x') # 有属性'x'吗? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有属性'y'吗? False >>> setattr(obj, 'y', 19) # 设置一个属性'y' >>> hasattr(obj, 'y') # 有属性'y'吗? True >>> getattr(obj, 'y') # 获取属性'y' 19 >>> obj.y # 获取属性'y' 19 如果试图获取不存在的属性,会抛出AttributeError的错误: >>> getattr(obj, 'z') # 获取属性'z' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyObject' object has no attribute 'z' 可以传入一个default参数,如果属性不存在,就返回默认值: >>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 404 也可以获得对象的方法: >>> hasattr(obj, 'power') # 有属性'power'吗? True >>> getattr(obj, 'power') # 获取属性'power' <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn >>> fn # fn指向obj.power <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>> >>> fn() # 调用fn()与调用obj.power()是一样的 81 面向对象高级编程 Python内置的@property装饰器就是负责把一个方法变成属性调用的: 可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性: class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth 上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。 __str__ 和 __repr__ 我们先定义一个Student类,打印一个实例: >>> class Student(object): ... def __init__(self, name): ... self.name = name ... >>> print(Student('Michael')) <__main__.Student object at 0x109afb190> 打印出一堆<__main__.Student object at 0x109afb190>,不好看。 怎么才能打印得好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了: >>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Michael')) Student object (name: Michael) __str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。 解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法: class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__
–
评论前必须登录!
注册