13 继承
面向对象三大特征:封装、继承、多态
1.继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”,所谓继承就是使现有的类无需编码便可以拥有原有类的方法和属性。
被继承的类可以称之为父类、基类、超类。继承的类可以称之为子类、派生类。派生和继承是一体两面,从父类向子类看就是派生,从子类向父类看就是继承。子类和父类的关系可以用“is a”类表示,即子类是父类的一种,是一个更具体、更加强大的父类。python支持单继承和多继承。
继承的优点:
- 可以简化代码,减少冗余度
- 提高了代码的可维护性
- 提高了代码的安全性
1.1 单继承
所谓的单继承就是一个子类只有一个父类。子类会继承父类所有的属性和方法。
- 私有成员在子类中无法直接使用
- 子类对象无法直接使用继承自父类的私有成员
- 子类方法和父类方法重名,通过子类对象调用的是子类方法
#语法:
class 子类名(父类名):
pass
注意:object是Python中所有类的父类【一般情况下,如果一个类没有指明的父类,默认它的父类为object】
-
构造函数的继承
class Animal: def __init__(self,name,age): self.name = name self.__age =age .... class Dog(Animal): def __init__(self,name,age,gender): #调用父类构造方法的好处,简化了子类构造方法代码,实现了代码复用 #1. super(当前类名,self).__init__(实参列表) #实参不要带self # super(Dog,self).__init__(name,age) # 2. super().__init__(实参列表)#实参不要带self # super().__init__(name, age) #3. 父类名.__init__(self,其它参数) Animal.__init__(self,name,age) self.gender = gender ....
-
方法的改写
class Animal: def __sleep(self): print('睡饱饱') def eat(self): print('吃饭饭') def sleeping(self): self.__sleep() .... class Dog(Animal): #改写继承自父类的方法 def eat(self): # Animal.eat(self) super().eat() print('香香的大骨头') #添加新方法 def bark(self): print('别跑,爷咬不死你') # print(self.age) dog = Dog('泰迪',5,'公') dog.eat() #优先调用子类方法 dog.sleeping() #子类没有,则调用父类方法,一直找到object都没有,则报错
-
方法添加
1.2 多继承
一个子类可以有多个父类。语法:
class 子类类名(父类1,父类2....):
子类类体
2.类成员
类成员包括:类属性和类方法,不同于成员属性和成员方法属于对象,类属性和类方法属于类,可以不实例化对象,直接通过类名调用,这些类成员为所有对象共有,一般用于对象间通信。
-
类属性
class Person: #类属性 name = '无名氏' gender = '男' __age = 0 #私有类属性 def __init__(self,name,age): self.name = name #实例属性或成员属性 self.__age = age #实例化对象 zhang = Person('张三',20) #类属性调用方式 #调用方式:类名.属性名 对象.属性名 print(Person.name) #类属性可以不实例化对象调用 print(zhang.gender) print(Person.__age) 不能通过类名直接调用 #访问的优先级 成员属性高于类属性 #如果成员属性名和类属性名冲突,优先访问成员属性,成员属性屏蔽类属性 #应该尽量避免成员属性和类属性重名 print(zhang.name) #张三 #删除对象的属性 del zhang.name print(zhang.name) #无名氏 访问的是类属性
-
类方法和静态方法
- 类方法属于类,为所有对象共有,可以通过类名或对象调用
- 类方法可以用来访问类属性
- 静态方法属于类的作用域内的全局函数
- 类方法和静态方法的区别,类方法第一个参数是类对象,由系统传入,静态方法没有
- 共同点:静态方法和类方法都可以通过类名直接调用,属于类,不属于对象
class Date:
def init(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod #类方法
def date_from_string(cls,date_string):
'''
:功能:根据传入字符串创建日期对象
:param cls 类对象,和类名作用一样
:param date_string: 日期字符串,格式必须是yyyy-mm-dd
:return: 日期对象
'''
year,month,day = tuple(map(int,date_string.split('-')))
return cls(year,month,day)
@staticmethod
def is_valid_date(date_string):
year,month,day = tuple(map(int,date_string.split('-')))
return year >= 0 and 1 < month <= 12 and 0< day <=31
d1 = Date.date_from_string('2018-05-29')
print(d1.year,d1.month,d1.day)
print(Date.is_valid_date('2018-5-29'))
3 实例成员的动态绑定(了解)
实例的属性和方法都可以动态绑定,也就是可以在程序运行期间可以给程序增加功能,这对于那些静态语言来说无异于天方夜谭,但对python来说一切皆有可能
class Cat:
pass
cat = Cat()
#1.动态绑定属性
cat.name = 'tom'
定义一个函数,第一个参数要是self
def walk(self):
print(self.name)
cat.walk = walk
cat.walk(cat) #怪胎
#2.给对象绑定方法
from types import MethodType #引入types模块中的MethodType
cat.walk = MethodType(walk,cat) #给cat对象绑定walk方法
cat.walk()
#3.给类绑定实例方法 ,所有对象都可以使用
Cat.walk = walk
cat1 = Cat()
cat1.name = 'jerry'
cat1.walk()
4. 系统魔术方法
4.1 __init__
和__new__
-
__ new__ 用于创建一个对象,有返回值;
__new__
是类方法;__ init__ 用于初始化对象,没有返回值 -
__new__
默认参数是cls,系统传递的是类名,__init__
默认参数是self,系统传递的是当前对象 -
__ new__ 先于__ init__ 执行
class Dog: def __new__(cls, *args, **kwargs): print('new方法在执行') // return super().__new__(cls,*args, **kwargs) #必须通过父类的__new__创建对象 return object.__new__(cls,*args, **kwargs) def __init__(self,name,age): self.name = name self.__age = age print('init方法在执行')
4.2 算数运算符重载
在python中自定义类的对象也可以象系统类型一样完成+、-、*、/、索引、切片等运算,这有赖于python类有运算符重载功能。
class MyTime:
def init(self,hour,minute,second):
self.hour = hour
self.minute = minute
self.second = second
def __add__(self, other):
if not isinstance(other,self.class):
raise TypeError('Not a MyTime')
second = self.second + other.second
minute = self.minute + other.minute + second // 60
hour = (self.hour + other.hour + minute // 60) % 24
return self.__class__(hour,minute % 60,second % 60)
#+=
def __iadd__(self, other):
self = self.__add__(other)
def __str__(self):
return "{}:{}:{}".format(self.hour,self.minute,self.second)
t1 = MyTime(5,34,45)
t2 = MyTime(8,45,34)
res = t1 + t2 #t1.__add__(t2)
print(res)
t1 += MyTime(3,20,10)
print(t1)
4.3 迭代器
如果想让一个类用于for-in 循环则必须实现__ iter__ 和__ next__ 方法
class Fib:
def __init__(self):
self.x = 0
self.y = 1
def __iter__(self):
return self #返回当前对象
def __next__(self):
self.x ,self.y = self.y, self.x + self.y #迭代公式
if self.x > 1000:
raise StopIteration()
return self.x
4.4 属性监听
__getattr__
如果访问了类中不存在(包括继承的属性)的属性,会自动调用该方法__setattr__
通过对象设置属性值时,会自动调用该方法
4.5 __call__
如果一个类实现了__call__(slef, [,*args [,**kwargs]])
方法,则该类的对象可以象函数一样调用。它是实现类装饰器的基础
class Demo:
def __call__(self, *args, **kwargs):
print("我是函数,不过是假冒的")
d1 = Demo()
d1()
- 类装饰器
class Decrator:
def __call__(self, func):
def inner():
func()
print("*"*50)
return inner
@Decrator()
def test():
print("我是你们的小可爱")