面向对象之继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
继承是一种创建新类的方式,在Python中,新创建的类可以继承一个或多个父类,父类又称为基类或超类,新建的类称为子类或派生类。
Python中类的继承分为单继承和多继承。
1 class BaseClass: # 父类 2 pass 3 4 class BaseClass2: # 父类 5 pass 6 7 class SubClass(BaseClass): # 继承了一个父类的子类 8 pass 9 10 class SubClass2(BaseClass,BaseClass2): # 继承了两个个父类的子类 11 pass
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
class Animal: def __init__(self, name, aggr,blood): self.name = name self.aggressivity = aggr self.blood = blood def eat(self): print('%s is eating'%self.name) class Dog(Animal): pass class Person(Animal): pass egg = Person('egon',10,1000) flash = Dog('金毛',50,1000) egg.eat() flash.eat()
class Animal: def eat(self): print('eating...') def drink(self): print('drinking...') def sleep(self): print('sleep...') class Cat(Animal): def say(self): print('喵喵喵') class Dog(Animal): def say(self): print('旺旺旺')
查看所有的父类:
print(Cat.__bases__) print(Animal.__bases__) # 如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
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)
super
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该使用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值。
在python3中,子类执行父类的方法也可以直接用super方法,关于super()的详细介绍请参考你不知道的super
class A: def hahaha(self): print('A') class B(A): def hahaha(self): super().hahaha() #super(B,self).hahaha() #A.hahaha(self)
# 以上三种方法功能相同
print('B') a = A() b = B() b.hahaha() super(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 Animal: def __init__(self, name, aggr, blood): self.name = name # 人和狗都有名字 self.aggr = aggr # 人和狗都有攻击力 self.blood = blood # 人和狗都有血量 def eat(self): # 人和狗都有可以吃 print('%s is eating' % self.name) class Person(Animal): def __init__(self, name, aggr, blood, money): super(Person, self).__init__(name, aggr, blood) # 执行父类中的__init__ self.money = money # 派生的新属性 金钱 def attack(self, enemy): # 派生的新方法 攻击 print('%s 攻击了 %s' % (self.name, enemy.name)) enemy.blood -= self.aggr class Dog(Animal): def __init__(self, name, aggr, blood, kind): # 执行父类中的__init__ super().__init__(name, aggr, blood) self.kind = kind # 派生的新属性 品种 def bite(self, enemy):# 派生的新方法 咬人 print('%s 咬了 %s' % (self.name, enemy.name)) enemy.blood -= self.aggr Tom = Person('Tom', 500, 2000, 300) dog = Dog('dog', 700, 2000, '哈士奇') # Tom.eat() # dog.eat() Tom.attack(dog) print(dog.blood) dog.bite(Tom) print(Tom.blood)
接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
from abc import abstractmethod,ABCMeta class Payment(metaclass=ABCMeta): # 接口类 @abstractmethod def payment(self,money): raise NotImplementedError class ApplyPay(Payment): def payment(self, money): print('Apple 支付了 %s' % (money)) class AliPay(Payment): def payment(self, money): print('支付宝 支付了 %s' % money) class WechatPay(Payment): def payment(self, money): print('微信 支付了 %s' % money) def payment(pay_obj, money): # 归一化设计 pay_obj.payment(money) ali = AliPay() # ali.payment(1000) apple = ApplyPay() # apple.payment(100) payment(ali, 200) payment(apple, 200) wechat = WechatPay() payment(wechat, 300)
抽象类
from abc import abstractmethod, ABCMeta class Operate(metaclass=ABCMeta): @abstractmethod def read(self): f = open() f.read() f.close() @abstractmethod def write(self): f = open() f.read() f.close() class File(Operate): def read(self): super().read() def write(self): super().write() class Disk(Operate): def read(self): super().read() def write(self): super().write()
接口类与抽象类:
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
接口类:不实现具体的功能,可以多继承
抽象类:可以实现简单的功能,最好单继承
面向对象之多态
多态指的是一类事物有多种形态
动物有多种形态:人,狗,猪
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
面向对象之封装
什么是封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
封装的优点
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
封装原则
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有变量和私有方法
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
class Teacher: __role = 'teacher' # 私有静态属性 def __init__(self,name,pwd): self.name = name self.__pwd = pwd # 私有属性 self.__passwd() def __passwd(self): # 私有方 法 print(self.__pwd) alex = Teacher('alex','3714') print(alex.__pwd) # __pwd是私有属性,在外部不能直接调用 print(alex.__dict__) # 打印指定对象的所有属性 alex._Teacher__pwd = 3838438 # 可通过这种方法调用__pwd。但不建议去调用 print(alex._Teacher__pwd)
class User: __role = 'user' # 私有静态属性 def __init__(self, username, password): self.username = username self.__password = password # 私有属性 def __pwd(self): # 私有方法 self.__sex = 'male' print(self.__password) tom = User('tom', 'acer123') # print(tom.__password) # 报错 AttributeError: 'User' object has no attribute '__password' print(tom.__dict__) # tom的所有属性 {'username': 'tom', '_User__password': 'acer123'} print(tom._User__password) # acer123 tom._User__pwd() print(User._User__role) tom._User__pwd() # 先执行__pwd方法才会生成__sex属性 print(tom._User__sex) tom.__age= 20 print(tom.__age) # 只有在类中定义的以双下滑线开头属性和方法才是私有方法/属性。
class Base: def __hide(self): # _Base__hide print('Base') class Son(Base): def __hide(self): # _Son_hide print('Son') def show(self): self.__hide() pass son = Son() son.show() # 在类中无论是定义还是调用私有方法/属性都会自动变形(_类名__属性名/方法名),也就是说在子类中无法调用父类中的私有方法/属性。因为子类和父类的类名不同。 # 可以通过__dict__查看对象所有的属性(对象.__dict__)
property
class Shop: discount = 0.9 def __init__(self, name, price): self.name = name self.__price = price @property # 将类中的方法伪装成属性 def price(self): return self.__price * Shop.discount @price.setter # 使price方法可以想属性那样去修改值 apple.price = 190 def price(self, new_price): self.__price = new_price apple = Shop('apple', 150) print(apple.price) # 双十一活动 Shop.discount = 0.7 apple.price = 190 print(apple.price)
staticmethod (静态方法)
class Staticmethod_Demo(): role = 'dog' @staticmethod def func(): # 不用接收参数 print("当普通方法用") Staticmethod_Demo.func()
classmethod (类方法)
class Classmethod_Demo(): role = 'dog' @classmethod def func(cls): print(cls.role) Classmethod_Demo.func()
小结:
普通方法:必须接收一个对象,可以使用对象的属性和类的属性
静态方法:没有必须接受的参数,方法不需要用对象的属性和类的属性
类方法:必须接收一个类,方法不需要使用对象的属性,但可以使用类的属性。
最后的小结:
对象:对象包括属性和方法。属性只是作为对象的一部分变量,方法则是存储在对象内部的函数。方法和其他函数的区别在于方法总是将对象作为自己的第一个参数。这个参数一般称为self。(staticmethod和classmethod)
类:类代表对象的集合(或一类对象),每个对象都有一个类。类的主要任务是定义它的实例会用到的方法。
多态:多态是实现将不同类型和类的对象进行同样对待的特性——不需要知道对象属于哪个类就能调用方法。
封装:对象可以将他们的内部状态隐藏(或封装)起来。在一些语言中,这意味着对象的状态(属性)只对自己的方法可用。在Python中,所有的对象都是公开可用的,但是我们最好不要去修改隐藏的属性。