python 基础笔记
程序员文章站
2022-04-23 16:31:32
一、继承'''1、什么是继承 继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类 继承描述的是一种“遗传”的关系:子类可以重用父类的属性在python中的继承注意两点: 1. 在python中支持一个子类同时继承多个父类, 2. python中类分为两种: 新式类:但凡继承... ......
一、继承
''' 1、什么是继承 继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类、父类、超类 继承描述的是一种“遗传”的关系:子类可以重用父类的属性 在python中的继承注意两点: 1. 在python中支持一个子类同时继承多个父类, 2. python中类分为两种: 新式类:但凡继承object的类,以及该类的子类。。。都是新式类 在python3中一个类如果没有继承人类类,默认继承object类,即python3中所有的类都是新式类 经典类: 没有继承object的类,以及该类的子类。。。都是经典类 在python2中才区分新式类与经典类 2、为何要用继承 减少代码冗余
3、如何用继承 class parent1(object): pass class parent2: pass class subclass1(parent1,parent2): pass print(subclass1.__bases__) # 2、在继承的背景下,属性查找的优先级 #当类是经典类时,多继承的情况下,在要查找的属性不存在时,会按照深度优先的方式查找下去 #当类是新式类时,多继承的情况下,在要查找的属性不存在时,会按照广度优先的方式查找下去
继承解决的是类与类之间的代码冗余问题,一定是一个类是另外一个类的子类,总结对象之间的相似之处得到类,总结类与类之间的相似之处就得到了类们的父类
多继承背景下属性查找的顺序:对象-》对象的类-》按照从左往右的顺序一个一个的分支找下去
# 一旦出现菱形继承问题,新式类与经典类在属性查找上的区别是
# 新式类:广度优先查找,在最后一个分支查找*类
# 经典类:深度优先查找,在第一个分支就查找*类
# 在子类派生出的新方法中重用父类功能的方式一:
# 指名道姓地访问某一个类的函数
# 注意:
# 1. 该方式与继承是没有关系的
# 2. 访问是某一个类的函数,没有自动传值的效果
class oldboypeople: school='oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class oldboystudent(oldboypeople): # stu1,'李特丹',18,'female' def __init__(self,name,age,sex,num=0): oldboypeople.__init__(self,name,age,sex) #oldboypeople.__init__(stu1,李特丹',18,'female') self.score=num def choose_course(self): print('%s is choosing course' %self.name) class oldboyteacher(oldboypeople): def __init__(self,name,age,sex,level): oldboypeople.__init__(self,name,age,sex) self.level=level def score(self,stu,num): stu.score=num stu1=oldboystudent('李特丹',18,'female') #oldboystudent.__init__(stu1,'李特丹',18,'female') print(stu1.__dict__) tea1=oldboyteacher('egon',18,'male',10) ##oldboyteacher.__init__(tea1,'egon',18,'male',10) print(tea1.__dict__)
在子类派生出的新方法中重用父类功能的方式二:只能在子类中用
在python2:super(自己的类名,对象自己)
在python3:super()
调用super()会得到一个特殊的对象,该特殊的对象是专门用来引用父类中的属性的,!!!完全参照mro列表!!!
注意:
# 1. 该方式与继承严格依赖于继承的mro列表
# 2. 访问是绑定方法,有自动传值的效果
class oldboypeople: school='oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class oldboystudent(oldboypeople): def __init__(self,name,age,sex,num=0): # oldboypeople.__init__(self,name,age,sex) #oldboypeople.__init__(stu1,李特丹',18,'female') super(oldboystudent,self).__init__(name,age,sex) self.score=num def choose_course(self): print('%s is choosing course' %self.name) class oldboyteacher(oldboypeople): def __init__(self,name,age,sex,level): super().__init__(name,age,sex) self.level=level def score(self,stu,num): stu.score=num #例子 class a: def test(self): print('a.test()') super().test() class b: def test(self): print('from b') class c(a,b): pass obj=c() print(c.mro()) #[<class '__main__.c'>, <class '__main__.a'>, <class '__main__.b'>, <class 'object'>] obj.test() ''' a.test() from b '''
类的继承有两层意义:
1.改变 2.扩展
组合指的是一个对象拥有某一个属性,该属性的值是另外一个类的对象
利用组合可以减少类与类之间代码冗余
二、多态
1 多态
多态指的是同一种事物的多种形态
2 多态性:
可以在不用考虑对象具体类型的情况下而直接使用对象
优点:归一化,简化对象的使用
多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样
python中的标准类型就是多态概念的一个很好的示范
import abc class animal(metaclass=abc.abcmeta): @abc.abstractmethod def speak(self): pass @abc.abstractmethod def run(self): pass # 抽象基类:是用来指定规范,但凡继承该类的子都必须实现speak和run,而名字必须叫speak和run # 注意:不能实例化抽象基类 animal() class people(animal): def speak(self): print('say hello') def run(self): pass class dog(animal): def speak(self): print('汪汪汪') def run(self): pass class pig(animal): def speak(self): print('哼哼哼哼哼') def run(self): pass obj1=people() obj2=dog() obj3=pig() # obj1,obj2,obj3都是动物 obj1.speak() obj2.speak() obj3.speak() def speak(animal): animal.speak() speak(obj1) speak(obj2) speak(obj3) obj1=[1,2,3] obj2='hello' obj3={'x':1} print(obj1.__len__()) print(obj2.__len__()) print(obj3.__len__()) print(len(obj1)) print(len(obj2)) print(len(obj3))
三、封装
1 什么是封装
装就是将数据属性或者函数属性存放到一个名称空间里
封指的是隐藏,该隐藏是为了明确地区分内外,即该隐藏是对外不对内(在类外部无法直接访问隐藏的属性,而在类内部是可以访问)
2 为何要封装
1. 封数据属性:???
2. 封函数属性:???
3 如何封装???
在类内定义的属性前加__开头
第一个层面的封装:类就是麻袋,这本身就是一种封装
第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问
第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)
class people: __country='china' #_people__country='china' __n=111 #_people__n=111 def __init__(self,name): self.__name=name #self._people__name=name def run(self): print('%s is running' %self.__name) #self._people__name print(people.__country) obj=people('egon') print(obj.__name) print(obj.run) obj.run() print(people.__dict__) print(people._people__country) print(obj.__dict__) print(obj._people__name)
需要注意的问题:
1. 这种隐藏只是一种语法上的变形,并没有真的限制访问
2. 这种变形只在类定义阶段检测语法时变形一次,类定义阶段之后新增的__开头的属性不会发生变形
3. 在继承中,父类如果不想让子类覆盖自己的方法,可以在该方法前加__开头
封装的真实意图:
把数据属性或函数属性装起来就是为了以后使用的,封起来即藏起来是为不让外部直接使用
1.封数据属性:把数据属性藏起来,是为了不让外部直接操作隐藏的属性,而通过类内开辟的接口来间接地操作属性,我们可以在接口之上附加任意的控制逻辑来严格控制使用者对属性的操作
2. 封函数属性: 隔离复杂度
四、类方法与装饰器
类中定义的函数有两大类(3小种)用途,一类是绑定方法,另外一类是非绑定方法
# 1. 绑定方法:
# 特点:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
# 1.1 绑定给对象的:类中定义的函数默认就是绑定对象的
# 1.2 绑定给类的:在类中定义的函数上加一个装饰器classmethod
# 2. 非绑定方法
# 特点: 既不与类绑定也不与对象绑定,意味着对象或者类都可以调用,但无论谁来调用都是一个普通函数,根本没有自动传值一说
class foo: def func1(self): print('绑定给对象的方法',self) @classmethod def func2(cls): print('绑定给类的方法: ',cls) @staticmethod def func3(): print('普通函数') obj=foo() obj.func1() print(obj) foo.func2() # 绑定方法 print(obj.func1) print(foo.func2) # 非绑定方法 print(obj.func3) print(foo.func3)