类
类,就是一种事物,具有共性。比如dict,人,熊猫
对象,类的实例化后所产生的,具有个性,比如{1:‘vf’},春哥,团团。
申明类:
class 类名: '类的文档字符串'#类的静态属性
def __init__(self,参数)
实例化所需要的参数代码
def 功能
面向对象小结——定义及调用的固定模式
class 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
class Person: country = 'China' def __init__(self,name,sex,age,hobby): self.name=name self.sex=sex self.age=age self.hobby=hobby def func(self): print(dir(self)) p1=Person('alex','male',68,'study') print(dir(Person)) print(dir(p1)) print(p1.__dict__) print(Person.__name__) print(Person.__doc__) print(Person.__base__) print(Person.__bases__) print(Person.__module__) print(Person.__class__) print(Person.__dict__) #输出的结果为 '''
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'country', 'func'] ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'country', 'func', 'hobby', 'name', 'sex'] {'hobby': 'study', 'sex': 'male', 'name': 'alex', 'age': 68} Person None <class 'object'> (<class 'object'>,) __main__ <class 'type'> {'__init__': <function Person.__init__ at 0x0000000000832620>, 'country': 'China', '__doc__': None, '__dict__': <attribute '__dict__' of 'Person' objects>, '__module__': '__main__', 'func': <function Person.func at 0x00000000008326A8>, '__weakref__': <attribute '__weakref__' of 'Person' objects>} '''
面向对象的组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
面向对象的命名空间
class name_space: code='utf-8' def __init__(self,name,sex,age): self.name=name self.age=age self.sex=sex lhy=name_space('lhy','female',18) dxx=name_space('dxx','male',23) print(name_space.code)#utf-8 print(lhy.code)#utf-8 print(lhy.age)#18 #print(name_space.name)#报错 #name_space.__dict__['code']='unicode'#报错 name_space.code='unicode' print(name_space.code)#unicode print(lhy.code)#unicode lhy.__dict__['code']='gbk' print(lhy.__dict__)#gbk print(lhy.code)#gbk print(name_space.code)#unicode
lhy.__dict__.pop('code')#删除code
print(lhy.code)#unicode
类的静态变量是可以通过 类.属性名来改变的,但是不可以通过类.__dict__来进行改变。同时静态属性的改变影响对象的静态属性,应为这个属性是绑定在类中并指向一个内存地址的
对象,可也以改变静态属性的。对象名.属性来改变,但是不会影响其他对象以及类的这静态属性的,相当于自己在自己的命名空间开了一新的的空间,不再是绑定关系,删除之后,才会恢复绑定关系
再看代码
class group: hobby=['eat','drink','swim'] wjw=group jjs=group print(group.hobby)#print(group.hobby) print(wjw.hobby)#print(group.hobby) print(jjs.hobby)#print(group.hobby) wjw.hobby.pop() print(group.hobby)#['eat', 'drink'] print(wjw.hobby)#['eat', 'drink'] print(jjs.hobby)#['eat', 'drink']
类里的名字有 类变量(静态属性量)+ 方法名(动态属性)
对象里的名字 对象属性
对象 —— > 类
对象找名字 : 先找自己的 找类的 再找不到就报错
对象修改静态属性的值
对于不可变数据类型来说,类变量最好用类名操作
对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的
面向对象的三大特征:继承,多态,封装
继承
单继承
父类又称超类,基类
子类又称派生类
class A: def fun(self): print('A') pass class B: def fun(self): print('B') pass class A_son(A): def fun(self): print('A_son(A)') pass class AB_son(A,B): def fun(self): print('AB_son(A,B)') pass print(A_son.__bases__)#(<class '__main__.A'>,) 表示A的子类 print(AB_son.__bases__)#(<class '__main__.A'>, <class '__main__.B'>)表示A和B的子类
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
继承与抽象:向抽象在继承。
请看代码
class People: def eat(self): print('%s eat a bread'%self.name) def sleep(self): print('%s is slepping'%self.name) def look_for_sex(self): print('%s is %s' % (self.name, self.sex)) class Xiaoming(People): def __init__(self,name,sex): self.name=name self.sex=sex class Xiaohong(People): def __init__(self,name,sex): self.name=name self.sex=sex xiaoming=Xiaoming('xiaoming','male') xiaohong=Xiaohong('xiaohong','female') xiaoming.eat()#xiaoming eat a bread xiaohong.look_for_sex()#xiaohong is female
注意:此时的父类定义时没有传参数,故可以直接调用
派生
子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类)。
需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
class A: def func(self): print('it is father function') class B(A): def func(self): print('it is son function') b= B() b.func()#it is son function
父类中没有的属性 在子类中出现 叫做派生属性
父类中没有的方法 在子类中出现 叫做派生方法
只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错
如果父类 子类都有 用子类的
如果还想用父类的,单独调用父类的:
父类名.方法名 需要自己传self参数
super().方法名 不需要自己传self
在外部调用父类方法是super().方法名 需要自己在()中填写(子类名,对象名)
内部调用(子类名,self)
注意区分内外掉用区别
super().hahaha() 等价super(B,self).hahaha(),一般用前面的
正常的代码中 单继承 === 减少了代码的重复
继承表达的是一种 子类是父类的关系
关于继承传参数
class A: def __init__(self,name,sex,age): self.name=name self.age= age self.sex=sex print('A传参数完成') class B(A): def __init__(self,name,age,sex,hobby): super().__init__(name,age,sex) # super(B, self).__init__(name, age, sex) #也可以这么写,这样就能统一self self.hobby=hobby print('hobby传参完成') a =B('alex',16,'male','study') #结果 #A传参数完成 #hobby传参完成
多继承:
多继承中,我们子类的对象调用一个方法,默认是就近原则,找的顺序是什么?
经典类中 深度优先
新式类中 广度优先
python2.7 新式类和经典类共存,新式类要继承object
python3 只有新式类,默认继承object
经典类和新式类还有一个区别 mro方法只在新式类中存在
类名.mro() 查找多继承的顺序
super 只在python3中存在
super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的
砖石继承
抽象类与接口类
接口类 : python原生不支持
抽象类 : python原生支持
规范 :接口类或者抽象类都可以
接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java
抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java
接口类
from abc import ABCMeta,abstractmethod class start_game(metaclass=ABCMeta): @abstractmethod def start(self,num):pass class LOL(start_game): def start(self,num): print('%s second will be open Lol'%num) class DNF(start_game): def start(self,num): print('%s second will be open DNF' % num) class QQ(start_game): def begin(self,num): print('%s second will be open QQ' % num) lol=LOL() lol.start(5)#5 second will be open Lol dnf=DNF() dnf.start(6)#6 second will be open DNF qq = QQ() qq.begin(5)#TypeError: Can't instantiate abstract class QQ with abstract methods start
解析
接口内的多继承
from abc import abstractmethod,ABCMeta class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self):pass class Walk_Animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self):pass class Tiger(Walk_Animal,Swim_Animal): def walk(self): pass def swim(self): pass class OldYing(Fly_Animal,Walk_Animal):pass class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass
抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' with open('filaname') as f: pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
#输出结果
#文本数据的读取方法
#硬盘数据的读取方法
#进程数据的读取方法
#file
#file
#file
抽象类 : 规范
一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中
抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化
java :
java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题
python
python中没有接口类 :
python中自带多继承 所以我们直接用class来实现了接口类
python中支持抽象类 : 一般情况下 单继承 不能实例化
且可以实现python代码
多态
python 天生支持多态属于动态强类型语言
鸭子类型
list tuple
不崇尚根据继承所得来的相似
我只是自己实现我自己的代码就可以了。
如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型
list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的
优点 : 松耦合 每个相似的类之间都没有影响
缺点 : 太随意了,只能靠自觉
强类型语言 多态
python 语言 鸭子类型
封装
广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
只让自己的对象能调用自己类中的方法
狭义上的封装 —— 面向对象的三大特性之一
属性 和 方法都藏起来 不让你看见
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
解析
class conformation: def __init__(self,name,sex,age): self.__name=name self.sex=sex self.__age=age def __func(self): return self.__age+7 def function(self): print(self.__func()) xiaoming = conformation('xiaoming','male',19) print(xiaoming.sex) # print(xiaoming.name)#报错 print(xiaoming._conformation__name) print(xiaoming.__dict__) #{'sex': 'male', '_age': 19, '_conformation__name': 'xiaoming'} print(xiaoming._conformation__func()) xiaoming.function() '''输出结果 male xiaoming {'_conformation__name': 'xiaoming', 'sex': 'male', '_conformation__age': 19} 26 26 '''
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
会用到私有的这个概念de场景
1.隐藏起一个属性 不想让类的外部调用
2.我想保护这个属性,不想让属性随意被改变
3.我想保护这个属性,不被子类继承