python全栈开发从入门到放弃之面向对象的三大特性
组合
class Course: def __init__(self,name,period,price): self.name = name self.period = period self.price = price class Birth: def __init__(self,year,month,day): self.year = year self.month = month self.day = day class Teacher: def __init__(self,name,salary,boy_friend,python): self.name = name self.salary = salary self.bf = boy_friend self.course = python python = Course('python','6 months',20000) egg = Teacher('egon',200,'yuan',python) egg_birth = Birth(1965,2,2) print(egg_birth.year) egg.birth = egg_birth print('***',egg.birth.year)
面向对父类象的三大特性
继承
什么是继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可成为基类或超类,新建的类成为派生类或子类
python中类的继承分为:单继承和多继承
class Person:pass #父类 class Dog:pass #父类 class Person1(Person):pass #单继承,基类是Person,派生类是Person1子类 class Dog1(Dog,Person):pass #python支持多继承,用逗号分隔开多个继承的类
查看继承
print(Person1.__bases__) #__bases__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 (<class '__main__.Person'>,) print(Dog1.__bases__) (<class '__main__.Dog'>, <class '__main__.Person'>) #Dog1有两父类
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现
class Person:pass class Dog:pass class Person1():pass class Dog1():pass print(Person1.__bases__) (<class 'object'>,) print(Dog1.__bases__) (<class 'object'>,)
继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪狗这三个类比较像的部分抽取成父类
抽象最主要的作用划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
继承与重用性
可以看到代码很长,name,leve,attack,heal都是重复的代码 class Person: role = 'person' def __init__(self,name,leve,attack,health_point): self.name=name self.leve=leve self.attack=attack self.heal=health_point def walk(self,dog): print("老曹发动技能撕咬") dog.heal=dog.heal-self.attack print("凯子狗还剩生命值%s"%dog.heal) class Dog: role='动物' def __init__(self,name,leve,attack,health_point): self.name=name self.leve=leve self.attack=attack self.heal=health_point def aggressivity(self,person): while True: print("郭世平发动攻击撕咬") person.heal -=self.attack print("刘武还剩生命值%s"%person.heal) if person.heal <= 0: print("刘武被郭世平打死了") break add = Person('刘武',1,1000,10000) edd = Dog('郭世平',2,100,100000) add.walk(edd) edd.aggressivity(add) ===========================第二部分 上述代码不难看出生命值、登记、攻击力、都是动物跟人都具有的功能,而我们却分别在动物和人的类中编写了两次,如果使用继承的思想如下实现: class Anim: def __init__(self,name,leve,attack,health_point): self.name = name self.leve = leve self.attack = attack self.heal = health_point class Person(Anim): role = 'person' def walk(self,dog): print("老曹发动技能撕咬") dog.heal=dog.heal-self.attack print("凯子狗还剩生命值%s"%dog.heal) class Dog(Anim): role='动物' def aggressivity(self,person): while True: print("郭世平发动攻击撕咬") person.heal -=self.attack print("刘武还剩生命值%s"%person.heal) if person.heal <= 0: print("刘武被郭世平打死了") break add = Person('刘武',1,1000,10000) edd = Dog('郭世平',2,100,100000) add.walk(edd) edd.aggressivity(add)
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时,我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会遗传A的所有属性(数据属性和函数属性),实现代码重用
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大节省了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是缩短了软件开发周期,对大型软件开发来说,意义重大。
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def bite(self, people): ''' 派生:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity class Person(Animal): ''' 人类,继承Animal ''' def attack(self, dog): ''' 派生:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity egg = Person('egon',10,1000) ha2 = Dog('二愣子',50,1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
注意:像ha2.life_value之类的属性引用,会先从实例中找到life_value然后去类中找,然后再去父类中找..直到找到最顶级的父类。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值.
在python3中,子类执行父类的方法也可以直接用super方法.
class A: def hahaha(self): print('A') class B(A): def hahaha(self): #super(B,self).hahaha() #A.hahaha(self) super().hahaha() print('B') a = A() b= B() b.hahaha()
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def __init__(self,name,breed,aggressivity,life_value): super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法 self.breed = breed #派生出了新的属性 def bite(self, people): ''' 派生出了新的技能:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) #super().eat() print('from Dog') class Person(Animal): ''' 人类,继承Animal ''' def __init__(self,name,aggressivity, life_value,money): #Animal.__init__(self, name, aggressivity, life_value) #super(Person, self).__init__(name, aggressivity, life_value) super().__init__(name,aggressivity, life_value) #执行父类的init方法 self.money = money #派生出了新的属性 def attack(self, dog): ''' 派生出了新的技能:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity def eat(self): #super().eat() Animal.eat(self) print('from Person') egg = Person('egon',10,1000,600) ha2 = Dog('二愣子','哈士奇',10,1000) print(egg.name) print(ha2.name) egg.eat()
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师
>>> class Teacher: ... def __init__(self,name,gender): ... self.name=name ... self.gender=gender ... def teach(self): ... print('teaching') ... >>> >>> class Professor(Teacher): ... pass ... >>> p1=Professor('egon','male') >>> p1.teach() teachin
那么问题又来了,多继承呢?
-
是否可以继承多个类
-
如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
1、Python的类可以继承多个类,Java和C#中则只能继承一个类
2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
-
当类是经典类时,多继承情况下,会按照深度优先方式查找
-
当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
经典类多继承 class D: def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
新式类多继承 class D(object): def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar()
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
三、多态
Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
Python伪代码实现Java或C#的多态 class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
Python “鸭子类型” class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)
二、封装
其实这仅仅只是一种变形操作
类中所有双下划线开头的名称如_x都会自动变形成:_类名__x的形式
class Parent: def __init__(self): #在父类调了func() 这里就是Son.func()又会去Son类找func self.func() def func(self): print("Parent func") class Son(Parent): #子类没有去找父类 def func(self): #找到func就调用,没有则又回去调父类的func print("Son func") s=Son() #调了儿子的类 用私有变量----------------------------------- class Parent: def __init__(self): #在父类调了func() 不会去Son类里面找了只会使用自己私有的 self.__func() def __func(self): print("Parent func") #所以输出Parent func class Son(Parent): #子类没有去找父类 def __func(self): print("Son func") s=Son() #调了儿子的类 ''' 输出结果: Parent func '''
class Goods: __discount = 0.8 #类的私有属性 def __init__(self,name,price): self.name = name self.price = price def goods_price(self): return self.price * Goods.__discount #内部可以随意用私有属性 banana = Goods('banana',2) print(banana.goods_price()) print(Goods.__dict__) #查看类的属性 print(Goods._Goods__discount) #外部调就必须加类名 ''' 输出结果: 1.6 {'__module__': '__main__', '_Goods__discount': 0.8, '__init__': <function Goods.__init__ at 0x000002029FA9C8C8>, 'goods_price': <function Goods.goods_price at 0x000002029FA9C950>, '__dict__': <attribute '__dict__' of 'Goods' objects>, '__weakref__': <attribute '__weakref__' of 'Goods' objects>, '__doc__': None} 0.8 '''
总结:
私有的:类属性 对象属性 方法
编程私有的:__名字
在类内都是照常使用
在类外部就变形成为:_类名__名字
定义私有~的原因
不让外部的人瞎调
类中的属性变方法
特性
1.property
from math import pi #引入3.14 class Circle: def __init__(self,radius): self.radius = radius @property #area = property(area) def area(self): return pi*self.radius*self.radius @property def perimeter(self): return 2*pi*self.radius c = Circle(10) #传值 print(c.area) print(c.perimeter) #不用添加括号可以直接调用 ''' 输出结果: 314.1592653589793 62.83185307179586 '''
from urllib.request import urlopen class Web_page: def __init__(self,url): self.url = url self.__content = None @property def content(self): #content 内容 if self.__content: #做了一个什么转换 _Web_page__content #if 是否有值有 return self.__content #判断是否有值,有则直接返回了 else: self.__content = urlopen(self.url).read() #做缓存 相当于 return self.__content mypage = Web_page('http://www.baidu.com') print(mypage.content)
class Num: def __init__(self,*args): print(args) if len(args) == 1 and (type(args[0]) is list or type(args[0]) is tuple): self.members = args[0] else: self.members = args @property def sum(self): return sum(self.members) @property def average(self): return self.sum/len(self.members) @property def min(self): return min(self.members) @property def max(self): return max(self.members) nums = Num([1,2,3]) print(nums.sum) print(nums.average) print(nums.min) print(nums.max) num2 = Num(4,5,6) print(num2.sum) print(num2.average) print(num2.min) print(num2.max)
总结:
property装饰器
@property把一个类中的方法 伪装成属性
obj.func()
obj.func -->属性
2.函数名.
class Goods: __discount = 0.8 #类的私有属性 def __init__(self,name,price): self.name = name self.__price = price @property def price(self): if hasattr(self,'__price'): return self.__price * Goods.__discount else: raise NameError @price.setter def price(self,new_price): if type(new_price) is int: self.__price = new_price #给私有变量赋上新的值 apple = Goods('apple',10) print(apple.price) apple.price = 20 #给私有变量赋上新的值 print(apple._Goods__price) #把值传给price ''' 输出结果: 10 20 '''
总结:
因为属性不能被修改
@funcname.setter
obj.func = new_value 调用的是被@funcname.setter装饰器装饰的方法
被@property装饰的方法名必须和被@funcname.setter装饰的方法同名
3.类名.deleter
class A(): def __init__(self,name): self.__name=name @property def name(self): #可以直接调函数 return self.__name #然后把私有变量给返回 @name.deleter def name(self): del self.__name #删除私有变量 a=A('hehe') print(a.name) del a.name #执行A类的@name.deleter print(a.name) #因为已经删除了找不到值了所以会报错
总结:
#@funcname.deleter
#在执行del obj.func 的时候会调用被这个装饰器装饰的方法(同名)