看到这个题目,很多人会问什么是面向对象编程,也许也有人说我只知道面向过程编程,那么我们来说说这两种编程。通俗一点讲面向过程就是你想干嘛,就直接写个功能函数来实现你想做的事,它面向的是以动作为主导,通过函数或者方法来实现;而面向对象主导因素是对象,实现的不是函数,而是“类”。把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class)。类就是对对象的抽象,而对象是类的实例。那么下面我们主要针对类的定义和特征(封装、继承、多态)进行分析。
在python中使用'class’关键字定义类,并以冒号结束。然后在类中定义一些属性和通过之前学习过的函数来定义方法,这样我们就可以把对象的动态特征描述出来。具体使用如下:
class 类名(object):
# __init__是一个特殊方法用于在创建对象时进行初始化操作
def __init__(self, 参数):
初始化代码
代码如下所示:
- class Teacher(object):
- # __init__是一个特殊方法用于在创建对象时进行初始化操作
- # 通过这个方法我们可以为老师对象绑定name和age两个属性
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def teach(self, course_name):
- print('%s正在教%s.' % (self.name, course_name))
当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。代码如下:
- def main():
- # 创建教师对象并指定姓名和年龄
- teacher1 = Teacher('诸葛亮', 38)
- # 给对象发teach消息
- teacher1.teach('Python程序设计')
- if __name__ == '__main__':
- main()
类的三种方法,分别是类方法、普通方法和静态方法,如下是这三种方法的概念:
静态方法: 用 @staticmethod 装饰的不带 self 参数的方法叫做静态方法,类的静态方法可以没有参数,可以直接使用类名调用。
普通方法: 默认有个self参数,且只能被对象调用。
类方法: 默认有个 cls 参数,可以被类和对象调用,需要加上 @classmethod 装饰器。
在这里主要聊聊静态方法和类方法,现在我们先说静态方法,从定义中我们知道静态方法不属于任何一个对象的。例如,我们定义一个“三角形”类的时候,我们会定义很多相关的方法,如计算周长和面积,但是我们还有一个判断三条边长是否可以构成三角形的方式,显然它不属于对象方法。为了解决这种问题,我们使用静态方法来处理,代码如下:
from math import sqrt class Triangle(object): def __init__(self, a, b, c): self._a = a self._b = b self._c = c @staticmethod def is_valid(a, b, c): return a + b > c and b + c > a and a + c > b def perimeter(self): return self._a + self._b + self._c def area(self): half = self.perimeter() / 2 return sqrt(half * (half - self._a)*(half - self._b) * (half - self._c)) def main(): a, b, c = 3, 4, 5 # 静态方法和类方法都是通过给类发消息来调用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('无法构成三角形.') if __name__ == '__main__': main()
其实类中还有一种方法和静态方法比较类似,它就是类方法。它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示:
from time import time, localtime, sleep class Clock(object): """数字时钟""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour self._minute = minute self._second = second @classmethod def now(cls): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) def show(self): """显示时间""" return '%02d:%02d:%02d' % (self._hour, self._minute, self._second) def main(): # 通过类方法创建对象并获取系统时间 clock = Clock.now() while True: print(clock.show()) sleep(1) if __name__ == '__main__': main()
类和类之间的关系有三种:is-a、has-a和use-a关系
is-a关系也叫继承或泛化,比如动物和猫的关系属于继承关系
has-a关系通常称之为关联,比如学校和教师的关系属于关联关系
use-a关系通常称之为依赖,比如人和空气的关系属于依赖关系
这里我们主要举例讲解has-a和use-a关系,is-a关系在继承和多态中举例说明,代码如下:
#has-a关系 class Person: def play(self, tools): tools.run() print('终于能打游戏了') class Phone: def run(self): print('王者荣耀已经登陆') PH = Phone() p = Person() p.play(PH) #use-a关系 class School: def __init__(self, name): self.teach_list = [] def join_us(self,teach): self.teach_list.append(teach) def to_teach(self): for t in self.teach_list: t.work() class Teacher: def __init__(self, name): self.name = name def work(self): print(f'{self.name}在上课') x = School('清华大学') t1 = Teacher('李老师') t2 = Teacher('刘老师') x.join_us(t1) x.join_us(t2) x.to_teach()
继承是啥,继承就是在原有的类基础上创建新类,让新类拥有原有类的属性和方法,这就是继承。提供继承信息的叫做父类,也叫超类或基类;得到继承信息的叫做子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。继承的例子如下:
class Animal(object): def __init__(self): self._name = "" self._age = 0 @property def age(self): return self._age @age.setter def age(self, age): self._age = age @property def name(self): return self._name @name.setter def name(self, name): self._name = name def show(self): print("%s, %d" % (self._name, self._age)) class Dog(Animal): def __init__(self): Animal.__init__(self) self._age = 1 self._name = "狗" class Cat(Animal): def __init__(self): super().__init__() self._name = "猫" self._age = 2 dog = Dog() dog.show() cat = Cat() cat.show()
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态。多态代码如下:
from abc import ABCMeta, abstractmethod class Animal(object, metaclass=ABCMeta): def __init__(self): self._name = "" self._age = 0 @abstractmethod def show(self): pass class Dog(Animal): def __init__(self): Animal.__init__(self) self._age = 1 self._name = "狗" def show(self): print("%s, %d 旺旺" % (self._name, self._age)) class Cat(Animal): def __init__(self): super().__init__() self._name = "猫" self._age = 2 def show(self): print("%s, %d 喵喵" % (self._name, self._age)) dog = Dog() dog.show() cat = Cat() cat.show()
在上面的代码中,我们将`Animal`类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。python通过`abc`模块的`ABCMeta`元类和`abstractmethod`包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,`Dog`和`Cat`两个子类分别对`Animal`类中的`show`抽象方法进行了重写并给出了不同的实现版本,当我们调用该方法时,这个方法就表现出了多态行为。
在其他高级语言(c++,c#)中,对属性和方式都有访问权限(也称为可见性),主要有三种访问权限:私有的(private)或受保护的(protected)和 公开的(public)。但python中的属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望是私有的,则在给其命名时可以用两个下划线作为开头。对属性还可以通过使用@property包装器来包装getter和setter方法来对属性的访问既安全又方便。代码如下:
class Pet(object): def __init__(self, name, age): self._name = name self._age = age @property def age(self): return self._age @age.setter def age(self, age): self._age = age @property def name(self): return self._name @name.setter def name(self, name): self._name = name def __make_voice(self): print("%s, %d 哈哈哈哈" % (self._name, self._age)) def show(self): print("%s, %d" % (self._name, self._age)) pet1= Pet("狗", 1) pet1.show() pet1.name = "猫" pet1.age= 5 pet1.show() # AttributeError: 'Pet' object has no attribute '__make_voice' #pet1.__make_voice()
Python动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。例如:
class Person(object): # 限定Person对象只能绑定_name, _age和_gender属性 __slots__ = ('_name', '_age', '_gender') def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def play(self): if self._age <= 16: print('%s正在玩飞行棋.' % self._name) else: print('%s正在玩斗地主.' % self._name) def main(): person = Person('诸葛亮', 22) person.play() person._gender = '男' # AttributeError: 'Person' object has no attribute '_is_gay' # person._is_gay = True
联系客服