面向对象编程是当今非常流行的编程方法,其较为正确的说法如下:
把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。我们在进行面向对象的编程中,需要把一切东西看成是对象,然后抽取这些对象身上的共有的点,把这些共有点都提取出来之后,就变成了一个类。
'''
例1:python中类的定义及使用
'''
class Student(object):
# __init__是一个特殊方法用于在创建对象时进行初始化操作
# 通过这个方法我们可以为学生对象绑定name和age两个属性
def __init__(self, name, age):
self.name = name
self.age = age
def study(self, course_name):
print('%s 正在学习 %s' %(self.name, course_name))
# PEP 8要求标识符的名字用全小写多个单词用下划线连接
# 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识)
def watch_movie(self):
if self.age < 18:
print('%s 只能看奥特曼 ' %self.name)
else:
print('%s 可以看很多电影' %self.name)
def main():
# 创建学生对象并指定姓名和年龄
stu1 = Student('uzi',20)
stu2 = Student('coco', 15)
# 给对象发study消息
stu1.study('数据库技术')
# 给对象发watch_moive消息
stu1.watch_movie()
stu2.study('高中数学')
stu2.watch_movie()
if __name__ == '__main__':
main()
在python中可以使用class关键字定义类,然后在类中通过函数来定义方法,这样就可以将对象的动态特征描述出来。
在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,例子如下所示。
'''
例2:python中的私有变量和私有方法
'''
class Test:
def __init__(self,foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
test.__bar() # 'Test' object has no attribute '__bar'
print(test.__foo) #'Test' object has no attribute '__foo'
if __name__ == '__main__':
main()
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们。
'''
例3:python中的访问私有变量和私有方法
'''
class Test:
def __init__(self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('11')
test._Test__bar()
print(test._Test__foo)
if __name__ == "__main__":
main()
在一个类中,如果将类中的变量设置为私有,并不建议这样做,但是将类中的变量直接暴露在外,这样也是不可取的做法。建议将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。
'''
例4:python中的getter方法和setter方法
'''
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age
# 访问器 getter方法
@property
def name(self):
return self._name
# 访问器 getter方法
@property
def age(self):
return self._age
# 修改器 - setter方法
@age.setter
def age(self,age):
self._age = age
def play(self):
if self._age<18:
print('%s 在玩斗兽棋' % self._name)
else:
print('%s 在玩牌九' % self._name)
def main():
person = Person('赌怪', 19)
person.play()
person.age = 11
person.play()
# person.name = '55k' #can't set attribute
if __name__ =='__main__':
main()
对于一个类而言,我们给以在这个类中定义很多的成员变量,因此python又是一门动态语言。但是如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。__slots__只对它当前所在的类生效。
'''
例5:python中__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('赌怪',18)
person.play()
person._gender = '男'
# person._is_gay = True #'Person' object has no attribute '_is_gay'
if __name__ == '__main__':
main()
在前几个例子中,我们用到的都是类方法,但是对于有些问题,可能需要提前验证。比如对于输入三角形的三条边计算该三角形的面积,在计算之前必须要判断这三条边能够构成一个三角形。此时,我们就需要使用静态方法来解决这类问题。
'''
例5:使用静态方法解决问题的事例
'''
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_vaild(a, b, c):
return a + b > c and a + c > b and b + c >a
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_vaild(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()
在python中,还有与静态方法相似的类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。
'''
例6:使用类方法创建对象
'''
from time import time,sleep,localtime
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 run(self):
#走字
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
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)
clock.run()
if __name__ == '__main__':
main()
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
类与类之间存在很多关系,主要有继承、关联和依赖三种。
继承也叫做泛化,比如学生和人、公交车和交通工具之间都叫做继承关系,当一个类继承另一个类的时候,它既可以使用原有的方法,又可以增加自己独特的方法,也可以叫做is-a。
关联关系也是一种常用的关系,比如车轮和车子的关系等;如果是整体和部分的关联,称为聚合关系;如果整体进一步负责部分的生命周期,即整体和部分是不可分割的,同时同在也同时消亡,称为合成关系,也可以叫做has-a关系。
依赖关系是一种use-a关系比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
在面向对象的编程语言中,面向对象语言的三个特点分别是封装、继承、多态。在python语言中可以看到对于一个类,其中的相关操作我们在外部是无法看到的,类将一些处理方法封装在了类的内部。
如果现在有一个类,该类中的功能我们正好想在另一个类中用到,可以使用继承的方式,这样可以大大减少代码编写复用。提供继承信息的类称之为父类,也叫超类或基类;得到继承信息的类称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。很多继承后的类,可以替换原来的旧类,并且在此基础上多了新的功能,符合里氏替换原则。
'''
例7:python中类的继承
'''
class Person(object):
##人类
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 paly(self):
print('%s is playing ' %self._name)
def watch_mv(self):
if self._age >= 18:
print('%s观看犯罪片' %self._name)
else:
print('%s看小猪佩奇' %self._name)
class Student(Person):
#学生类
def __init__(self, name, age, garde):
super().__init__(name, age)
self._garde = garde
@property
def garde(self):
return self._grade
@garde.setter
def garde(self, garde):
self._garde = garde
def study(self, course):
print('%s的%s正在学习%s' %(self._garde, self._name, course))
class Teacher(Person):
#老师类
def __init__(self, name, age, title):
super().__init__(name, age)
self._title = title
@property
def title(self):
return self._title
@title.setter
def title(self):
self._title = title
def teach(self, course):
print('%s %s 正在讲授 %s' %(self._name, self._title, course))
def main():
stu = Student('小明', 15, '初二')
stu.study('数学')
stu.watch_mv()
t = Teacher('圣斗士', 28, '教授')
t.teach('英语')
t.watch_mv()
if __name__ == '__main__':
main()
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
'''
例8:python中类的多态实例
'''
from abc import ABCMeta,abstractmethod
class Pet(object, metaclass=ABCMeta):
#宠物类
def __init__(self, nickname):
self._nickname = nickname
@abstractmethod
def make_voice(self):
#发出声音
pass
class Dog(Pet):
#狗类
def make_voice(self):
print('%s :汪汪汪' %self._nickname)
class Cat(Pet):
#猫类
def make_voice(self):
print('%s:喵喵喵' %self._nickname)
def main():
pets =[Dog('旺财'), Cat('咪咪')]
for pet in pets:
pet.make_voice()
if __name__ == '__main__':
main()
在上面的代码中,Pet是一个抽象类,专门让其他类去继承它。在此通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。
在上面的例子中,Cat类和Dog类分别继承了Pet类,并且对于make_voice()给出了不同的处理方式,表现出了多态行为。
'''
例x:一个小数字时钟
Python time sleep() 函数推迟调用线程的运行,
可通过参数secs指秒数,表示进程挂起的时间
'''
from time import sleep
class Clock(object):
def __init__(self, hour = 0, minute = 0, second = 0):
'''
初始化方法:
hour 小时
minute 分钟
second 秒钟
'''
self._hour = hour
self._minute = minute
self._second = second
def run(self):
#钟表走字
self._second += 1
if self._second==60:
self._minute += 1
self._second = 0
if self._minute == 60:
self._hour += 1
self._minute = 0
if self._hour == 24:
self._hour = 0
def show(self):
#显示时间
print('%02d : %02d : %02d' %(self._hour, self._minute, self._second))
def main():
clock = Clock(23, 59, 55)
while True:
clock.show()
#。
sleep(1)
clock.run()
if __name__ == '__main__':
main()
参考资料:
[1]https://github.com/jackfrued/Python-100-Days
[2]《Python学习手册(第4版)》