1 面向过程编程
以指令为核心:围绕“正在发生什么”进行编写
面向过程:程序= 算法+ 数据结构
面向过程编程:程序具备一系列线性步骤,主体思想是代码做用于数据,以指令为核心,核心是设计算法,
2 面向函数的编程
3 面向对象的编程python
面向对象编程--object oriented programming ,简称OOP,把对象做为程序的基本单元,一个对象包含了数据和操做数据的函数
面向过程把函数继续切分称为子函数,来下降系统的复杂度。
面向对象编程(OOP)
程序 = 指令 + 数据/算法 + 数据结构
以数据为核心: 围绕“将影响谁”进行编写
面向对象编程(OOP):围绕数据以及为数据严格定义的接口来组织程序,用数据控制对代码的访问,面向数据,以及可以对这些数据采起的操做进行,代码执行过成,不能跳过数据
面向对象编程的核心概念:
全部编程语言的最终目的都是提供一种抽象方法
在机器模型(“解空间”或“方案空间”)与实际解决问题的问题模型(“问题空间”)之间,程序员必须创建一种联系
将问题空间中的元素以及他们在解空间中的标识物抽象为对象,并容许经过问题来描述问题而不是经过方案来描述问题
能够把实例(某个类的实例)想象成一种新型变量,他保存这数据,但能够对自身数据执行操做,实例能够对自身数据作操做程序员
定义了被多个同一类型对象共享的结构和行为(数据和代码)
类是抽象的,实例是具体的,类是(概念,抽象的概念模型),类自己是不能被操做的,须有实例化的对象才能操做
类内部有两个核心成员:代码和数据,都叫类成员
数据:成员变量或实例变量
成员方法: 简称为方法,是操做数据的代码,用于定义如何使用成员变量,所以一个类的行为和接口是经过方法来定义的
类:将同一种具体的物事的共同特性抽想出来的表现算法
实例是类的具体表现形式,类存在的目的就是为了实例化。类是抽象的,实例是具体的编程
calss 类名(父类):
类的内容
类后面有括号的称为新式类,没有括号称为经典类。
括号里面的内容是父类的名称,程序中,全部父类都是object设计模式
类的查看:python3.x
当类的实例化后,类的属性是不变的安全
类的数据属性是可变的,当在实例处修改类的数据属性时,其类自己的数据属性不变。数据结构
当类的数据属性发生改变时,本来已经改变的实例的数据属性不会再次改变,而新建立的实例的数据属性会发生改变app
#!/usr/local/bin/python3.6 #coding:utf-8 class MyClass: '''is example class''' x='abc' # 类的属性 def foo(self): # 类的属性,也是方法 return (self,'MyClass') myclass=MyClass() # 进行实例化操做 print (myclass.x) #调用类的属性 print (MyClass.x) # 类调用类属性 print (MyClass.foo(1)) # 类调用类方法 print (myclass.foo()) # 调用类的方法 print (type(myclass)) # 打印类型 print (id(MyClass)) # 打印类的实例地址 print (id(myclass)) # 打印类的内存地址
结果以下 dom
其实例化都的内存地址和类自己的内存地址不一样,且实例能够调用类的属性和方法,类自身也能够进行相关的调用处理
在类中定义的函数教方法,类的方法中,要求第一个形参必须是self,而self其实是类实例化后的对象自己
实例化后得到的实例,是不一样的实例,即便使用相同的参数实例化,也获得不同的对象,其会先调用new 进行实例化,而后调用_init_ 进行初始化
实例化后,会自动调用__init__方法,这个方法的第一个参数必须留给self,其余参数随意
类方法的定义
类方法的调用
#!/usr/local/bin/python3.6 #coding:utf-8 class MyClass: '''is example class''' x='abc' # 类的属性 y='cbd' def __init__(self): # 初始化时进行打印,并显示结果,其__init__方法中不能return,其只能用作初始化 print ('init') def foo(self): return (self.x) def foo1(self): return (self.y) myclass=MyClass() # 对象实例化 print (myclass.foo()) print (myclass.foo1())
结果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=20): self.name=name # 此处的name始终和上述的self后面的name相对应,下面相同 self.x=age def showage(self): print ('{} is {}'.format(self.name,self.x)) # 此处self.x 始终和上面的相同,其中age至关于形参 tom=Person('tom') #对参数进行实例化操做 jerry=Person('jerry',10) # 对参数进行实例化 tom.showage() #打印其方法 jerry.showage() a=Person('a') # 其获取到的参数彻底相同 b=Person('a') print (a is b) # 其内存地址不一样 print (a==b) # 其获取结果不一样,
结果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 经过此处的参数,可修改类中的属性 self.name=x # 修改name的属性 self.age=y Person.x=x # 修改类属性 print (self.name,self.age,x) tom=Person('tom') tom.show('jerry',20) print (Person.x) # 其返回结果为修改类属性后的结果
结果以下
1 class._name_ 类的名称显示
2 class._doc_ 类的帮助文档
3 class._base_ 类的父类/基类
4 class._module_ 类的模块,当不是导入的模块的类时,其执行结果为main,当为模块导入时,其执行结果为模块名
5 class._dict_ 对象属性的字典,用于遍历和查找属性,每一个对象中保存这本身的属性,类的属性在类字典中,实例的属性在实例的字典中
6 class._class_ 对象的类型,__class__和type中的类型是相同的,在python3中相同,在早期的python2 中有某些事不相同的
7 class._qualname_ 类的限定名
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 经过此处的参数,可修改类中的属性 self.name=x # 修改name的属性 self.age=y Person.x=x # 修改类属性 print (self.name,self.age,x) tom=Person('tom') print (tom.__class__,Person.__class__) # 打印实例类型和类的类型 print (tom.__dict__,Person.__dict__) # 打印实例的属性字典和类的属性字典 print (tom.__qualname__,Person.__qualname__) # 打印实例的限定名和类的限定名,默认的,只有类有限定名
结果以下
如需输出,则须要
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 经过此处的参数,可修改类中的属性 self.name=x # 修改name的属性 self.age=y Person.x=x # 修改类属性 print (self.name,self.age,x) tom=Person('tom') print (tom.__class__.__qualname__) #此处经过__class__吊起该实例的类,经过类调用其限定名便可 print (isinstance(tom,Person)) #经过此处判断该类是不是该实例的类 print(isinstance(tom,tom.__class__))
结果
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 经过此处的参数,可修改类中的属性 self.name=x # 修改name的属性 self.age=y Person.x=x # 修改类属性 print (self.name,self.age,x) tom=Person('tom') jerry=Person('jerry',20) print (tom.__dict__,jerry.__dict__,Person.__dict__,sep='\n')# 打印实例和类的属性字典
结果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' age=3 height=170 def __init__(self,name,age=18): self.name=name self.age=age tom=Person('tom') jerry=Person('jerry',20) Person.age=30 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') #此处打印类和实例对象属性字典 print (Person.age,tom.age,jerry.age) # 结果为30,18,20 Person.height+=30 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') print (Person.height,tom.height,jerry.height) # 结果为200,200,200 tom.height=180 # 当此处给单独的对象添加了类的属性并赋值后,其对象tom的属性字典中的值也会随之变化 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') # 此处tom的字典属性中也增长了height的key,且其值为180 print (Person.height,tom.height,jerry.height) #结果为200,180,200 jerry.height+=30 # 此处添加了实例的属性后,其对象字典中的对象属性也会随之增长 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') # 此处jerry 的字典属性中也增长了height的key,且其值为230 print (Person.height,tom.height,jerry.height) # 结果为200,180,230
结果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' age=3 height=170 def __init__(self,name,age=18): self.name=name self.age=age tom=Person('tom') print (tom.__dict__['name'],tom.__dict__['age']) # 经过key调用对应的value,此处不能调用height,由于其不在tom属性字典中
结果以下
是类的,也是这个类全部实例的,其实例均可以访问到,是实例的,就是这个实例本身的,经过类访问不到。
实例能够动态的给本身增长一个属性,实例.dict[变量名]和实例.变量名均可以访问到
实例的同名变量会隐藏这类变量,或者说是覆盖了这类变量
实例属性的查找顺序
指的是实例使用.来访问属性,会先找到本身的dict,若是没有。则经过属性class找到本身的类,再去类的dict中找注意: 若是实例使用dict[变量名]访问变量,将不会按照上面的顺序查找变量了
经过类方法@classmethod 进行将类自己调用,其中cls 关键字就是表示类自己,
可使用类名.类方法(参数) 的形式进行调用。并使用return 返回结果
1 在定义类中,使用@classmethod 装饰器修饰的方法
2 必须至少传递一个参数,且第一个参数留给了cls,cls指代调用者及类对象自身
3 cls这个标识符能够是任何合法名称,可是为了易读,请不要修改
4 经过cls能够直接操做类的属性,其没法对实例的属性进行修改,由于其第一个传递的是类对象,类中也不能调用实例的方法
相关实现
#!/usr/local/bin/python3.6 #coding:utf-8 class Document: # 父类 def __init__(self,content): self.content=content def print(self): # 共有的属性 print (self.content) class Word(Document): pass class PrintableWord(Word): def print(self): # 子类本身的私有属性 print ("Word pinrt {}".format(self.content)) class Pdf(Document): pass # 子类自定义私有属性 print (PrintableWord.mro()) word=PrintableWord('test\nabc') word.print()
结果以下
实例以下
#!/usr/bin/poython3.6 #conding:utf-8 class MyClass: x='123' @classmethod # 类方法,此处传入的第一个值是类,cls.__name__调用的是这个类的名称,而cls.x调用的是这个类的私有属性 def my(cls): cls.pas='456' # 可经过此种方式在类中增长属性 print ('{}.x={}'.format(cls.__name__,cls.x),cls.pas) MyClass.my() # 此处是类的自调用 a=MyClass() # 实例化,其类的相关属性依然存在 a.my()
结果以下
1 在定义类中,使用@staticmethod 装饰器装饰的方法
2 调用时,不会隐式传入参数
3 静态方法,只是代表这个方法属于这个名称空间,函数归类在一块儿,方便管理组织
实例以下
#!/usr/bin/poython3.6 #conding:utf-8 class MyClass: def char(): #此处无语法错误,但其不建议如此写入 print ('char') @staticmethod # 此处是静态方法,用于保存类中的静态变量,其自己和实例无任何关系 def char1(): print ('char1') a=MyClass() # a.char() # 此处调用会出错,但下面的因为是静态方法,所以其调用不会出错 a.char1()
总结: 静态方法和类方法都属于类的属性
此处不推荐直接使用def char()的方式进行处理,而推荐使用@staticmethod,python中对静态方法的使用较少,
datetime 是在类上调用的方法,进而造出datetime对象而返回这个方法
python中常见的三种函数:
1 和实例相关的函数
2 和类相关的函数
3 通常的函数类的方法在类建立完成后便可进行调用,而实例的方法在类建立完成以后不能调用
类的方法调用
类几乎能够调用全部内部定义的方法,可是调用普通的方法时会报错,缘由是第一个参数必须是类的实例
实例几乎能够调用全部的方法,普通的函数的调用通常不可能出现,由于不容许这么定义
类方法:默认第一个参数是类自己,其能够被类调用,不能被实例调用
普通方法:默认第一个参数是实例自己,能够被类和实例进行调用
静态方法:默认第一个参数数传入的参数。只能被类自己进行调用,不能被实例调用
总结:
类除了普通方法外均可以调用,普通方法须要对象的实例做为第一参数
实例能够调用全部类中的方法(包括类方法,静态方法),普通方法传入实例自身,静态方法和类方法须要找到实例的类后进行相关的处理。
扩展:
在类中调用类自己的方法:
传统的输入年、月、日、的方法:
经过函数进行格式化的结果,经过在函数中调用类的方式实现
使用双下划线开头的属性名,就是私有属性
经过在类的构造时使用self.__x=x 来 获取类的私有属性。
默认的类的私有属性不能访问:
经过将其私有属性包装成方法进行使用,其是能够访问的。
实例以下:
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age): self.name=name self.age=age def char(self): if 0< self.age < 100: return self.age tom=Person('tom',50) print (tom.char()) jerry=Person('jerry',110) print (jerry.char()) # 此处的返回为空,由于没有知足的条件 print (jerry.age) # 可经过此种方式进行返回,所以其是没法真实控制的
结果以下
此处python提供了私有属性用于解决此种问题
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age): self.name=name self.__age=age # 私有属性,隐藏以后没法使用实例名.__age进行调用,必须将其抽象成一个方法并输出的方式进行处理 def char(self): if 0< self.__age < 100: return self.__age # def charget(self): return self.__age tom=Person('tom',50) print (tom.char()) jerry=Person('jerry',110) # print (jerry.__age) #此处没法经过__age的方法进行调用吗 print (jerry.charget()) #此处可有方法进行调用处理 print (jerry.__dict__) # 事实上,此处的私有属性在python中是修改了名称,其结果是实例名._类名称__age可进行调用,具体可参见__dict__获取,此处私有属性是在实例字典中存在的 print (jerry._Person__age) jerry._Person__age=300 #修改私有属性参数值,但其测试,显示中既然是私有属性,则不建议修改其属性值 print (jerry._Person__age) # 修改结果查看
结果以下
私有变量的本质: 类定义的时候,若是声明一个实例变量的时候,使用双下划线,python解释器会将其更名,转换成为实例名._类名__变量名,因此原来的名字访问不到了,私有属性是在实例的字典中的
在变量名前使用一个下划线,称为保护变量
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name): self._name=name # 保护变量,python开发人员约定俗称的,不可修改的,但实际其是能够被实例调用且修改的,但建议其使用方法进行封装处理 def printf(self): return self._name tom=Person('tom') print (tom._name) tom._name='jerry' print (tom.printf()) #修改后打印
结果以下
能够看出,这个_name属性根本就没改变名称,和普通的属性同样,解释器不作任何特殊处理,这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用,保护变量也是在实例的字典中的。
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name): self.name=name def __printf(self): #私有方法,其本质是对其名称作了修改,其方法是类的方法是Person.__printf return self.name tom=Person('tom') print (Person.__dict__) # print (tom.__printf()) #修改后打印 print (tom.__dict__) # 实例列表中无此方法 print (tom._Person__printf()) # 此处的调用先找到实例,而后没有这方法,而后找到类,找到该方法,
结果以下
在函数上的双下划线,是属于类的属性,方法内部(函数)的双下划线,是属于实例的属性,而不是类的属性
私有方法的本质
单下划线的方法只是开发者之间的约定,解释器不会作任何改变
双下划线的方法,是私有方法,解释器会更名,改变策略和私有变量相同,但其私有变量是在实例字典中,而私有方法倒是在类字典中
私有成员总结
在python中使用单下划线或者双下划綫来标识一个成员被保护或者私有化隐藏起来,可是,无论使用什么样的方式访问控制,都不能真正的阻止用户修改类的成员属性,python中没有绝对安全的保护成员或者私有成员所以,前面的下划线是一种警告或者提醒,请遵照这个约定,除非真的必要,不然不要修改随意使用成员保护或者私有成员。
属性装饰器的目的: 把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性。
通常方法
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=18): self.__name=name self.age=age def getname(self): #查看名称的属性 return self.__name def setname(self,name): # 修更名称属性 self.__name=name tom=Person('tom') print (tom.getname()) tom.setname('jerry') print (tom.getname())
结果以下
属性装饰器
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=18): self.__name=name self.age=age @property def getname(self): #查看名称的属性 return self.__name @getname.setter #修改属性值,此处的getname和上面的和下面的必须相同,不然会出现错误 def getname(self,value): self.__name=value @getname.deleter # 删除属性值,使用del self.__name 进行对属性的删除操做 def getname(self): #del self.__name print ('delete self.__name') tom=Person('tom') print (tom.getname) tom.getname='jerry' # 修改属性值 print (tom.getname) del tom.getname #外部调用执行删除程序
结果以下
第二种写法
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,chinese,english,history): self._chinese=chinese self.__english=english def seteng(self,value): # 只有getter属性,此中成为只读属性 self.__english=value #第二种写方式,初始化方法 eng=property(lambda self:self.__english,seteng ) # property 是只读的,此处的传入的值,eng是一个函数,第一个是gettter,第二个是setter stu=Person(90,100,110) print (stu.eng) stu.eng=200 #修改其值 print (stu.eng)
结果以下
经过此装饰器@property: 定义一个类方法为私有属性的名称;让用户能够直接访问, 但不能任意修改;
经过其装饰器的方法进行从新定义一个接受隐藏属性的范围来进行修改其值。
@属性名.seeter: 给属性赋值时先作判断; 当属性名=value会自动调用该函数
经过deleter 方法可使得当del 属性名, 会自动调用该函数并执行高函数下面的操做~~
特别注意: 这三个方法同名
property 装饰器
后面跟的函数名就是之后的属性名,它就是getter,这个必须有,有了它至少是只读属性setter装饰器
与属性名同名,且接受2个参数,第一个是self,第二个是将要赋值的值,有了它,属性可写deleter 装饰器
能够控制是否删除属性,不多用property装饰器必须在前,setter、deleterious装饰器在后
property 装饰器能经过简单的方式,把对象方法的操做编程对属性的访问,并起到了必定的隐藏做用
@property应用:
用于分页显示,此处传入两个值,一个值是第几页,另外一个是每页的数量,经过@property 能够进行对列表的操做。
类中能够定义del方法,称为析构函数(方法)
做用: 销毁类的实例的时候调用,用以释放占用的资源 ,其目标是销毁实例因为python实现了垃圾回收机制,这个方法不能肯定什么时候使用,有必要的时候,请使用del语句删除实例
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age=20): self.name=name self.__age=age def __del__(self): # 设置消亡实例 print ("delete instance {}".format(self.name)) a=Person('tom') del a # 调用消亡实例
结果以下
在其余面向对象的高级语言中,都有重载的概念,所谓的重载,就是同一个方法名,可是参数数量,类型不同,就是同一个方法的重载
python 没有重载,python不须要重载
python中,方法(函数)定义中,形参很是灵活,不须要指定类型,参数个数也不固定,一个函数的定义能够实现多种不一样的形式,所以python不须要重载。
python中只要标识符相同,则会被彻底覆盖,
1 随机数生成类,能够指定一批生成的个数,能够指定数值的范围,生成随机数
#!/usr/bin/poython3.6 #conding:utf-8 import random class randsum: @classmethod def getsrandsum(cls,min=1,max=10,num=10): return [random.randint(min,max) for _ in range(num)] print (randsum.getsrandsum()) print (randsum.getsrandsum(10,20,5))
结果以下:
2 随机生成随机数,并两两配对造成二维坐标系,把这些坐标组织起来,并进行打印
#!/usr/bin/poython3.6 #conding:utf-8 import random class RandomNum: def __init__(self,count=10,min=1,max=10): self.count=count self.min=min self.max=max self.g=self._generate() # 此处可以使用此种方式将内部的方法以属性的形式进行处理,供内部调用 def _generate(self): while True: yield [random.randint(self.min,self.max) for _ in range(self.count)] #c此处返回一个列表生成式,用于下面的next()进行调用 def genrate(self,count): self.count=count return next(self.g) # 此处经过此种方式进行处理,返回值为随机数的单个个体 a=RandomNum() for k,v in dict(zip(a.genrate(5),a.genrate(5))).items(): print ("(x={},y={})".format(k,v))
结果以下
3 记录车的品牌mark,颜色color,价格 price,速度等特征,并实现增长车辆信息,显示所有车辆信息的功能。
#!/usr/bin/poython3.6 #conding:utf-8 class Car: def __init__(self,mark,speed,color,price): self.mark=mark self.speed=speed self.color=color self.price=price class CarInfo: # 信息自己也是一个对象,一个容器的对象 def __init__(self): self.lst = [] def addcar(self,car:Car): #此处可调用上述的类 self.lst.append(car) def getall(self): return self.lst # 打印的格式化输出 ci=CarInfo()# 此处完成后,其基本的列表建立已经完成 car=Car('audi',100,'red',1500000) # 此处是初始化 ci.addcar(car) # 调用此初始化数据并增长 print (ci.getall()) # 查看
能够经过修改或者替换成员,使用者调用方没改变,可是,类提供的功能可能已经修改了,当项目须要修改较多的类时,又着急上线,修改起来比较麻烦时,须要经过在一个文件中指定某个类进行相关的操做来对其进行打补丁
猴子补丁
运行时,对属性进行动态替换
黑魔法,慎用
test 源文件
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,chinese,english,history): self.chinese=chinese self.english=english self.history=history def getscore(self): return (self.history,self.english,self.chinese) a=Person(70,80,90) print (a.getscore())
默认结果以下
补丁文件
test1
#!/usr/bin/poython3.6 #conding:utf-8 def getscore(self): # 其名称和相关的类对象名必须一致,不然会报错,此处返回是一个字典 return dict(chi=self.chinese,eng=self.english,his=self.history)
test2
#!/usr/bin/poython3.6 #conding:utf-8 from test import Person from test1 import getscore def monkeypatch4Persoon(): Person.getscore=getscore # 对类来改变属性,在类的DICT中有方法, student = Person(80,91,80) print (student.getscore()) monkeypatch4Persoon() student1 = Person(80,91,80) print (student1.getscore())
结果以下
组装: 将数据和操做组装到一块儿
隐藏数据:对外只暴露一些接口,经过接口访问对象
封装其实是把数据封装到摸个地方,之后再去调用在某处的内容或者数据
调用封装数据的方式
经过对象直接调用
其中init表示的是一个构造器,当类的实例化过程时会调用其方法,因为其是必选参数,所以在传值时必须与对应的个数相同,固然能够实例化多个对象
经过self在类内部对类中的数据进行调用
类的实例化
多复用,继承来的就不用本身写了
多继承少修改,OCP,使用继承来改变,来提现个性
基类和派生类
其中父类也叫基类
子类也叫派生类
父类
子类,其中没有定义类的属性和方法,只是继承了Class1 的属性
子类的实例化和结果,其彻底继承和父类的属性和方法:
当子类中有本身的构造函数时,以子类中的构造函数为准
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此处的A使用装饰器将方法封装成属性并进行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此处的B 继承了A的属性 x=456 def shout(self): # 此处的B对A的方法进行了重写 print ('B shout') class C(B): pass tom=C('tom') print (tom.name) tom.shout()
结果以下
默认的,私有属性不能直接调用,须要假装成方法方可调用。
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def shout(self): print ('B shout') jerry=A('tom') print (jerry.__dict__) tom=B('jerry') print (tom.__dict__) # 默认的A的私有属性会被B读取,并可进行访问,其方法亦可被访问 print (tom.getage)
结果以下
在类中,以双下划綫开始的方法称为私有方法:私有方法不能继承。
#!/usr/bin/poython3.6 #conding:utf-8 class A: def __init__(self,name,age): self.name=name self.age=age def __getname(self): #此处定义私有方法 return self.name class B(A): pass b=B('tom',30) print (b.__dict__) print (B.__dict__) a=A('jerry',20) print (a.__dict__) print (A.__dict__) # 私有方法是属于类的,不能被继承,
结果以下:
特殊属性和方法 | 含义 |
---|---|
_base_ | 类的基类 |
_bases_ | 类的基类元组 |
_mro_ | 显示方法查找顺序,基类的元组 |
mor() | 同上 |
_subclasses_() | 类的子类列表 |
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此处的A使用装饰器将方法封装成属性并进行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此处的B 继承了A的属性 x=456 def shout(self): # 此处的B对A的方法进行了重写 print ('B shout') class C(B): pass print (C.__base__) #显示此类的基类 print (C.__bases__) # 显示此类的基类,以元组呈现 print (C.__mro__) # 显示类的链表,由此类的父类开始查找 print (C.mro()) # 通上 print (A.__subclasses__()) # 显示此类的子类
结果以下
类和实例属性列表打印
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此处的A使用装饰器将方法封装成属性并进行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此处的B 继承了A的属性 x=456 def shout(self): # 此处的B对A的方法进行了重写 print ('B shout') class C(B): pass tom=C('tom') print (tom.__dict__) # 打印实例的字典,其中只有实例化的属性的列表 print (C.__dict__) # 打印C类的字典,C类无任何属性和方法,由于其是来自于B和A的 print (B.__dict__) # 打印B类的字典,其中有x类属性的定义,shout函数的定义 print (A.__dict__)# A中包括初始化函数等信息
以下
类和类属性只有一个,而实例和实例属性却有多个
凡是须要分红多个的,应该设计成类的实例属性,凡是须要一份的,应该设计成类的属性
一个子类继承了祖先的特征,但祖先的都是抽象的东西,只有对象才具备继承数据的能力,对于实例,不一样的须要设置各个单独的属性,须要放置在本身的属性列表中,若是须要一份,则放置在类属性上。
A.init(self,args)
1 经过 父类名.init(self,父类参数) 进行重写构造函数
经过super(本身类名称,self).init(形参)
优势: 不须要明确告诉父类的名称,若是父类改变,只需修改class 语句后面的继承关系便可,不用修改父类名称
实例化调用
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def shout(self): print ('A shout') class B(A): def shout(self): #此处定义覆盖了父类的shout print ('B') def shout(self): # 此处定义覆盖了自身的shout print (super()) # 显示super内容 print (super(B,self))# 此处至关于将B设置为self传输进去。等价与上面的super super().shout()#此处调用的是父类并调用了父类的shout a=B() a.shout()
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name # 此处是实例的初始化函数,固然要在其实例化后才能表现出该属性,该类中也找不到,固然在其子类中也不能找到 self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): self.age=age # 此处的传入后会修改为50,由于其是在修改以后传入A类中进行处理的 super().__init__(name,age) #新式类推荐使用的方式,此处默认传入self,须要送入指定参数,此处不准找指定self和父类的类名 self.__name=name+" "+'B' # 私有属性的修改是没法生效的,由于其私有属性的key和类名相关, # self.age=10 #覆盖共有属性,其以前的age是0,如今覆盖成10,由于实例的字典中的key是惟一的。此处由于在a.__init__(self,name)以后定义,所以其会 # 覆盖以前name传入的值 def shout(self): print ('B shout') super(B,self).shout() #此处是调用父类的,经过super能够方面的访问本身的祖先类,其查找顺序是找本实例,若经过本实例查找 #其类,经过类查找其父类并进行相关的操做 super().shout() # 与上述相同 x=B('jerry',30) # 此处传入的值是30,因为下面的覆盖,变成了10 x.shout() print (x.__dict__) #其私有属性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.age=age def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此处进行初始化,初始化后,其父类的公有属性将不能被调用 self.name=name #此处从新覆盖了共有属性name,由以前的tom修改为了jerry def shout(self): print ('B shout') tom=B('jerry') print (tom.name) print (tom.__dict__) #print (tom.age) # 此处是父类的属性,此处不能被调用
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.age=age def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此处进行初始化,初始化后,其父类的公有属性将不能被调用 self.cname=name #此处定义了公有属性cname def shout(self): print ('B shout') tom=B('jerry') print (tom.cname) # print (tom.name) #此处是父类的属性,不能被调用,由于子类进行了初始化 print (tom.__dict__) # 此处只有cname #print (tom.age) # 此处是父类的属性,此处不能被调用
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom'): self.name=name def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此处进行初始化,此处须要调用父类的方法,若不调用,则默认父类的私有方法不能使用,但共有方法能够被调用 self.cname=name #此处从新定义了共有属性cname, A.__init__(self,name) # 此处调用了父类的name def shout(self): print ('B shout') tom=B('jerry') print (tom.name) #此处name存在于A类中。而B中调用A.__init__(self,args) 致使其A中存在的属性在B中也一样存在 print (tom.cname) print (tom.__dict__)# 其共有方法中的name存在,也有本身的初始化的cname
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): #self.age=age # 此处的传入后会修改为30,由于其是在修改以后传入A类中进行处理的 A.__init__(self,name,age) #经过父类的调用来调用父类的属性,此中可调用父类的私有属性, self.__name=name+" "+'B' # 私有属性的修改是没法生效的,由于其私有属性的key和类名相关, self.age=10 #覆盖共有属性,其以前的age是0,如今覆盖成10,由于实例的字典中的key是惟一的。此处由于在a.__init__(self,name)以后定义,所以其会 # 覆盖以前name传入的值 def shout(self): print ('B shout') x=B('jerry',30) # 此处传入的值是30,因为下面的覆盖,变成了10 x.shout() print (x.__dict__) #其私有属性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name # 此处是实例的初始化函数,固然要在其实例化后才能表现出该属性,该类中也找不到,固然在其子类中也不能找到 self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): self.age=age # 此处的传入后会修改为50,由于其是在修改以后传入A类中进行处理的 A.__init__(self,name,age) #经过父类的调用来调用父类的属性,此中可调用父类的私有属性, self.__name=name+" "+'B' # 私有属性的修改是没法生效的,由于其私有属性的key和类名相关, # self.age=10 #覆盖共有属性,其以前的age是0,如今覆盖成10,由于实例的字典中的key是惟一的。此处由于在a.__init__(self,name)以后定义,所以其会 # 覆盖以前name传入的值 def shout(self): print ('B shout') x=B('jerry',30) # 此处传入的值是30,因为下面的覆盖,变成了10 x.shout() print (x.__dict__) #其私有属性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子类的初始化 self.name=name # 此处进行覆盖共有属性,其私有属性未处理 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此处只获取到了从新定义的name,而私有属性__age 不存在
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子类的初始化 self.__age=age # 此处进行覆盖私有属性,其私有属性未处理 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此处只获取到了从新定义的__age,而共有属性name则不存在
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子类的初始化 A.__init__(self,name,age) #调用父类方法,处理获取属性 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此处可获取父类中的共有属性和私有属性 print (b.name,b._A__age) # 已经进行了修改,,由于上面的def __init__(self,name,age): 中的age在实例化后已经被修改 print (b.getage)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子类的初始化 A.__init__(self,name,age) #调用父类方法,处理获取属性 self.__age=age # 重覆盖age,但没法完成,由于其私有属性和类名有关,其至关于增长 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此处可获取父类中的共有属性和私有属性 print (b.name,b._A__age) # 已经进行了修改,,由于上面的def __init__(self,name,age): 中的age在实例化后已经被修改 print (b.getage)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: @classmethod def class_method(cls): print (cls.__name__) # 此处打印类的名称 @staticmethod # 此处定义其静态方法 def static_method(): print ('A staticmethod') class B(A): @classmethod def class_method(cls): print (cls.__name__) @staticmethod def static_method(): print ('B staticmethod') class C(B): pass b=C() b.class_method() # 此处由于调用的是C类,所以其cls.__name__打印的是C而并不是是B b.static_method() # 此处的静态方法是B,由于C中未定义静态方法 print (B.__dict__) a=A() a.class_method() # 此处由于调用的时A类,所以cls.__name__打印的是A a.static_method() print (A.__dict__)
结果以下
静态方法会进行属性查找,找到及截至,类方法由于类的继承而进入该C类中,而C类调用其类方法后获得的名称固然是C本身,此处若只有A配置类属性,则B和C进行调用,则各自cls.name 仍然是各个实例的类,至关于本身在调用本身的类。此处大大的复用了,此处就是多态
#!/usr/bin/poython3.6 #conding:utf-8 class A: def __init__(self,name,age): self.name=name self.age=age def __getname(self): #此处定义私有方法 return self.name class B(A): def __init__(self,name,age): # 此处进行初始化,方法不能继承 self.name=name def __getname(self): # 此处属于重定义私有方法 return self.age b=B('tom',30) print (b.__dict__) print (B.__dict__) a=A('jerry',20) print (a.__dict__) print (A.__dict__) # 私有方法是属于类的,不能被继承,
结果以下
原则: OCP 原则,多继承,少修改
继承的用途: 加强基类,实现多态多态:在面向对象环境中,父类,子类经过继承联系到一块儿,若是能够经过一套方法,就能够实现不一样表现,这就是多态
一个类继承多个类就是多继承,它具备多个类的特征
多态是继承+多态
多态的弊端:
1 多继承很好的模拟了世界,由于事物不多是单一继承,可是舍弃简单,必然引入复杂性,带来了冲突
多继承的实现会致使编译器设计的复杂度增长,因此不少语言都舍弃了类的多继承。多继承可能带来二义性
解决方案
实现多继承的语言,须要解决二义性和,深度优先或广度优先
左边是多继承,右边是单继承
多继承带来的路径选择问题
python 使用MRO(method resolution order)解决了基类搜索顺序问题
历史缘由,MRO有三个搜索算法:
1 经典算法,按照定义从左到右,深度优先策略,2.2以前
左图的MRO是MyClass,D,B,A,C,A
新式类算法,经典算法的升级,重复的只保留最后一个2.2
左图的MRO是MyClass,D,B,C,A,object
C3 算法,在类被建立出来的时候,就计算出一个MRO有序列表,2.3以后,python3惟一支持的算法
左图中的MRO是MyClass,D,B,C,A,object 的列表
C3 算法解决的多继承的二义性
多继承的缺点
当类不少时,继承复杂的状况下,继承路径太多,很难说清什么样的继承路径
Python 语法是容许多继承,但python代码是解释器执行的,只有执行到的时候,才发现错误
团队协做开发,若是引入多继承,代码将不可控
无论编程语言是否支持多继承,都应当避免多继承
python2.x 里面支持经典类和新式类
python3.x 里面仅支持新式类
经典类,其能够不写父类。
新式类,其若是没有继承的父类则直接写object,必须写父类,若是有父类,则直接写父类
只有新式类支持mro() 方法
对于新式类,是广度优先
对于经典类,是深度优先
对于新式类,当调用D实例时,其结果是执行D的输出,当D中为pass 占位,其继承了B的输出,当B中的结果为pass 时,其会继承C中的输出,
对于经典类,当调用D实例时,其结果是执行D的输出,当D中为pass 占位,其继承了B的输出,当B中的结果为pass 时,其会继承A中的输出,由于B继承了A,由于其是深度优先。
继承时,共有的,子类和实例均可以随意访问,私有成员被隐藏,子类和实例不可直接访问,当私有变量所在的类内的方法中能够访问这个私有变量,python经过本身一套实现,实现和其余语言同样的面向对象的继承机制
属性查找顺序
实例的_dict_-----类的_dict_ -----父类的_dict_,若是搜索到这些地方后没有找到就会抛异常,先找到就当即返回了。
装饰器: 用装饰器装饰一个类,把功能给类附加上去,那个类须要,就装饰它
Mixin
文档Document 类是其余全部文档类的抽象基类,
Word,PDF类是Document的子类。
此处可抽象为两种类
1 共同的特性抽象成一个类,父类
2 其余不一样的特性组成其余的相关的属性,子类
Mxin 本质上是多继承实现的
mixin 提现的是一种组合的设计模式
在面向对象的设计中,一个复杂的类,每每须要不少功能,而这些功能有来自不一样的类提供,这些就须要不少组合在一块儿。
MIxin 类的使用原则:
Mixin 类中不该该显式的出现__init__初始化方法
Mixin 类一般不能独立工做,由于它是准本混入别的类中的部分功能实现
Mixin 类的祖先类也应该是Mixin类
Mixin类和装饰器
这两种方式都是可使用,看我的喜爱
若是须要继承就使用MixIn类的方法
1 子类中欲实现本身的功能本身处理,再也不父类中进行定义
#!/usr/local/bin/python3.6 #coding:utf-8 class Document: # 父类 def __init__(self,content): self.content=content def print(self): # 共有的属性 print (self.content) class Word(Document): pass class PrintableWord(Word): def print(self): # 子类本身的私有属性 print ("Word pinrt {}".format(self.content)) class Pdf(Document): pass # 子类自定义私有属性 print (PrintableWord.mro()) word=PrintableWord('test\nabc') word.print()
结果以下
2 经过父类中实现。子类若须要个性化需求,则本身修改
#!/usr/local/bin/python3.6 #coding:utf-8 def printable(cls): #此处传入的是类函数 def _print(self): # 此处的实例能够看做是这个类函数的参数 print (self.content,'装饰器') # 此处是计算的返回值 cls.print=_print # 此处对返回值进行赋值 return cls # 并返回该函数的执行结果 class Document: def __init__(self,content): self.content=content class Word(Document): pass class Pdf(Document): pass @printable class PrintableWord(Word): #此处先继承,后装饰 pass print (PrintableWord.mro()) pw=PrintableWord('test string') pw.print()
结果以下
另外一种方式,使用lambda 进行处理
#!/usr/local/bin/python3.6 #coding:utf-8 def printable(cls): # 此处是给对应的类增长相关的属性,使得其更加健壮 cls.print=lambda self: print (self.content) cls.NAME='name' return cls class Document: def __init__(self,content): self.content=content class Word(Document): pass class Pdf(Document): pass @printable #先继承,后装饰 class PrintableWord(Word): pass print (PrintableWord.mro()) pw=PrintableWord('test string') pw.print() print (pw.NAME)
结果以下
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class PrintablePdf(PrintableMixin,Pdf): # 此处继承了Mixin和Pdf两个类,此处是多继承,Mixin继承了object。而Pdf继承了Docment # 而Docment 继承了object,此处的做用是使用PrintableMixin覆盖以前的DOcment才是要求,通常的Mixin都是在最前面放的 pass print (PrintablePdf.mro()) #查看MRO的输出,以MRO的输出为准 Pdf=PrintablePdf('test\nabc') Pdf.print()
结果以下
调换位置以下
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class PrintablePdf(Pdf,PrintableMixin): # 此处继承了Mixin和Pdf两个类,此处是多继承,Mixin继承了object。而Pdf继承了Docment # 而Docment 继承了object,此处的做用是使用PrintableMixin覆盖以前的DOcment才是要求,通常的Mixin都是在最前面放的 pass print (PrintablePdf.mro()) #查看MRO的输出,以MRO的输出为准 Pdf=PrintablePdf('test\nabc') Pdf.print()
结果以下:
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class SuperPrintableMixin(PrintableMixin): # 此处使用类的继承来打印一个超类 def print(self): print ('~'*20) super().print() print('~'*20) class PrintablePdf(SuperPrintableMixin,Pdf): pass print (PrintablePdf.mro()) #查看MRO的输出,以MRO的输出为准 Pdf=PrintablePdf('test\nabc') Pdf.print()
结果以下
装饰器使用的是函数的方式实现的,其函数只能实现封装功能,而Mixin是一个类,其类可实现封装,继承,和多态的特性。