1.面向对象的定义:3.7新特性
面向过程 VS 面向对象
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在python 中面向对象的程序设计并不是全部。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
类:具有相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)
实例化:类——>对象的过程(这在生活中表现的不明显,我们在后面再慢慢解释)
类的定义,对象的实例化,类和对象的操作
class Person: #类名,类名都默认大写开头 country = 'China'#创造了一个只要是这个类就一定有的属性 #类属性 静态属性 def __init__(self,*args): #初始化方法,self是对象,是必须传参数 #print(self.__dict__) self.name = args[0] self.hp = args[1] self.aggr = args[2] self.sex = args[3] #print(self.__dict__) 可以把self看成一个字典,self后面的参数,是它的key, 类名()这里括号里的参数,传进来后是它的value #print(id(self)) #注意,init的方法不能写返回值 def walk(self,n): #此处叫做方法,self必须传,且在第一个,后面还可以传其他参数 #注意此处self是默认写法,且这个参数必须传但是如果这里写成 s或者其他名字也没关系,因为当alex传值进来的时候,其实就可以取到值了,这里只是个形参 #所以其实此处walk和__init__其实是两个独立函数,也可以放在__init__上面,但是别这么写 print('%s在散步,走了%s步'%(self.name,n)) #注意此处Person.name肯定是取值不到的,类比你问大家,人类的名字是什么,但是 可以调用类属性 Person.country alex = Person('gousheng',100,1,'不详') #一旦开始初始化,就调用了object里的 __new__方法,创建了self # print(alex) print(alex.__dict__)#查看所有的属性 # print(alex.name) # print(id(alex)) #alex和self内存地址id都是一样的,所以可以看成self就是alex alex.walk(5) #等价于 Person.walk(alex),这是一种简写.alex作为类的对象,可以调用类的方法,并自动把本身属性传进去 #然后本身自动传进去了,所以此时只需要再传一个n给 walk方法 print(Person.__dict__['country']) #因为可以用__dict__方法,所以可以像操作字典一样调用key value,但是类的字典不能改,对象才能改 #但是这样改静态属性 Person.country = 'others' print(alex.__dict__) #同时可以 alex.__dict__['name'] = 'gkx' 通过这样来改属性,但是一般不这么用 print(alex.name) alex.name = 'gkx' #一般这么修改 本质上是调用了__dict__方法 print(alex.name) #对象 = 类名() #过程: #类名() 首先 会创造出一个对象,创建了一个self变量 #调用init方法,类名括号里的参数会被这里接收 #执行init方法 #返回self,也就返回给了alex对象本身 #对象能做的事: #查看属性 #调用方法 #__dict__ 对于对象的增删改查操作,都可以通过字典的语法进行 #类名能做的事: #实例化 #调用静态属性(类属性) #__dict__ 对于类中的dict方法,只能看不能操作
#一般来说你的值就是属性,动作就是方法 class Person: def __init__(self,name,hp,aggr,sex): self.name = name self.hp = hp self.aggr = aggr self.sex = sex def attack(self,dog): #把狗的实例化传进来 dog.hp -= self.aggr print('%s被打了,掉了%s点血,剩下%s生命值'%(dog.name,self.aggr,dog.hp)) class Dog: def __init__(self,name,hp,aggr,kind): self.name = name self.hp = hp self.aggr = aggr self.kind = kind def bite(self, person): person.hp -= self.aggr print('%s被咬了,掉了%s点血,剩下%s生命值'%(person.name,self.aggr,person.hp)) alex = Person('alex',200,2,'不详') jin = Dog('jinlaoban',600,50,'teddy') alex.attack(jin) jin.bite(alex) # print(alex.__dict__)
#使用面向对象的情况 #当有几个函数,需要反反复复传入相同参数的时候,就可以考虑面向对象,这些参数都是对象的属性 #非常明显的处理一类事物 这些事物都有相似的属性和功能 class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def kanchai(self): print('%s,%s,%s,上山去砍柴'%(self.name,self.age,self.sex)) def kaiche(self): print('%s,%s,%s,开车去东北'%(self.name,self.age,self.sex)) def baojian(self): print('%s,%s,%s,去大保健'%(self.name,self.age,self.sex)) ming = Person('小明','10岁','男') wang = Person('老王','90岁','男') ming.kanchai() wang.kaiche() # import sys # print(sys.path) from math import pi class Circle: def __init__(self,radi): self.radi = radi def permi(self): return pi*self.radi*2 # print(2*pi*self.radi) def area(self): return pi*self.radi**2 # print(pi*self.radi**2) yuan = Circle(4) print(yuan.permi()) print(yuan.area())
class Foo: def func(self): #当类中没有 __init__ 也可以实例化,但是就没有办法给实例传属性了 #不能初始化self,此处的self只是一个普通参数,接收的是一个 空的实例 print('no init') f1 = Foo() f1.func()
#类可以定义两种属性 静态属性(类属性) 和 动态属性(方法) #类可以约束属性 class Course: language = 'Chinese' def __init__(self,teacher,course_name,period,price): self.teacher = teacher self.name = course_name self.period = period self.price = price print(Course.language) python = Course('egon','python','6months',20000) print(python.__dict__) #对象中有指向类的指针,当对象寻找language的时候,先在自身的内存地址找,没找到,再去类里面找 #但是类不能找对象的,Course.name调用不到的 #类中的静态变量,可以被对象和类调用 #我们知道 Course.language = 'English' #这样子是可以修改的 #但是 python.language = 'English' #也是可以运行成功的,但是此时,对象是无权更改 Course里的不可变数据类型的 #然后又在自己内部找不到 language去修改,所以 python这个对象在自己内部创建了 一个 language print(python.__dict__) #此时里面多了个language #此时python这个对象,就再也接收不到 类里的静态属性了,除非: del python.language #删除掉其内部的language才可以重新接收 #所以:对于不可变数据类型来说(str,int),最好是用 类 去调用,尽量不要从 对象 调用 #~~~~~~~可变和不可变的静态属性的区别~~~~~~~~~ #但是对于 可变数据类型 比如 language = ['Chinese'] python.language[0]='English'是可以改变 类 中对应的值的 #因为 对于列表来说,其内存地址肯定不变,里面是什么元素都无所谓 #对于 可变数据类型 对象的修改是共享,重新赋值是独立的 #小练习 class Foo: count = 0 def __init__(self): Foo.count += 1 f1 = Foo() print(Foo.count) f2 = Foo() print(Foo.count)
静态属性补充: 由于 self就可以看成是类的对象本身。对于类到的静态属性,在类外可以用 对象.static_name 来访问。自然在类中也可以 用 self.静态名 来访问了! >>> class A: x = 12 def px(self): print("A中的x",self.x) >>> a = A() >>> a.px() A中的x 12 >>>
#面向对象的命名空间:类和对象,分别调用什么属性。
#注意类的定义,如果__init__里的参数都给了默认值,那么在实例化的时候,可以不带参数。类似函数的形参方法及传参顺序
# 组合 :一个对象的属性值是另外一个类的对象 # alex.weapon 是 Weapon类的对象 import time class Person: def __init__(self,name,hp,aggr,sex): self.name = name self.hp = hp self.aggr = aggr self.sex = sex self.money = 0 def attack(self,dog): #把狗的实例化传进来 dog.hp -= self.aggr print('%s被打了,掉了%s点血,剩下%s生命值'%(dog.name,self.aggr,dog.hp)) def get_weapon(self,weapon): if self.money > weapon.price: self.money -= weapon.price self.weapon = weapon self.aggr += weapon.aggr class Dog: def __init__(self,name,hp,aggr,kind): self.name = name self.hp = hp self.aggr = aggr self.kind = kind def bite(self, person): person.hp -= self.aggr print('%s被咬了,掉了%s点血,剩下%s生命值'%(person.name,self.aggr,person.hp)) class Weapon: def __init__(self,name,aggr,njd,price): self.name = name self.aggr = aggr self.njd = njd self.price =price def beatdragon(self,person): if self.njd > 0: person.hp -= self.aggr*2 print('%s被打了,掉了%s点血,剩下%s生命值' % (person.name, self.aggr*2, person.hp)) self.njd -=1 alex = Person('alex',100,0.5,'不详') jin = Person('jinlaoban',500,100,'teddy') w = Weapon('打狗棒',100,3,998) alex.money += 1000 alex.attack(jin) time.sleep(0.5) alex.get_weapon(w) time.sleep(0.5) alex.attack(jin) time.sleep(0.5) alex.weapon.beatdragon(jin)
#组合 #1 可以在初始化中 令 self.name = Lei(name,args1,args2..) 括号里的值直接是具体的值 #2 可以在初始化中 令 self.name = name 然后后续中 把一个其他类的对象 传入 name 中 #3 可以在方法中,令 self.new = new 这个new为方法中传入的对象 #4 可以在运行过程中 令 self.xxx = 对象 #只要一发生,一个对象的属性值是另一个对象,则就是组合 #练习1 from cmath import pi class Circle: def __init__(self,r): self.r = r def area(self): return pi*self.r**2 def peri(self): return pi*self.r*2 # a = Circle(3) # print(a.area()) # print(a.peri()) class Ring: def __init__(self,inside_r,outside_r): self.inside_r = Circle(inside_r) self.outside_r = Circle(outside_r) def area(self): return self.outside_r.area()-self.inside_r.area() def peri(self): return self.inside_r.peri()+self.outside_r.peri() b = Ring(10,20) print(b.area()) print(b.peri()) #练习2 class Course: def __init__(self,course_name,period,price): self.name = course_name self.period = period self.price = price class Teacher: def __init__(self,name,course,sex,birh): self.name = name self.course = course self.sex = sex self.birh = birh self.course = Course('python', '6 month', 2000) class Birth: def __init__(self,year,month,day): self.year = year self.month = month self.day = day bb = Birth(1981,9,22) jing = Teacher('jing','python','femal',bb) print(jing.birh.year)
#什么是组合,什么时候使用组合
#一个类是可以没有 init 方法的,但是一旦没有,就没办法初始化self class Foo: def func(self): #当类中没有 __init__ 也可以实例化,但是就没有办法给实例传属性了 #不能初始化self,此处的self只是一个普通参数,接收的是一个 空的实例 print('no init') f1 = Foo() f1.func() print(f1.func) #<bound method Foo.func of <__main__.Foo object at 0x0000022110C02780>> #到目前为止,只有对象调用类的方法,才称为绑定方法 对象和类方法发生绑定关系 # 包 —— __init__ # import package —— 类的实例化的过程 # import time # time.time()
一.继承
#python3中的类 叫做 新式类 #继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类 #父类又称 基类,超类 子类又称 派生类 #一个父类可以被多次继承, 在python中一个子类可以继承多个父类 #只有子类能找到父类,父类不知道自己有什么子类 class A:pass class B:pass class A_son(A):pass class B_son(A,B):pass print(A_son.__bases__) #(<class '__main__.A'>,) print(A.__bases__) #(<class 'object'>,) 这个 object 是所有类的祖宗,没有继承父类的类,可以看作其父类是 object #在python3中所有的类都有父类,python3中的类又名 新式类 #也就说是说 class A:pass 写成 class A(object):pass 也是完全没问题的。 #小例子 # class Animal: # def __init__(self,name,aggr,hp): # self.name = name # self.aggr = aggr # self.hp = hp # class Dog(Animal): # pass # class Person(Animal): # pass # # jin = Dog('金老板',200,500) # print(jin.name) # 狗类 吃 喝 看门(guard) # 鸟类 吃 喝 下蛋(lay) class Animal: def __init__(self): print('执行Animal.__init__') self.func() def eat(self): print('%s eating'%self.name) def drink(self): print('%s drinking'%self.name) def func(self): print('Animal.func') class Dog(Animal): #当自己没有init方法的时候,使用父类的 init #此时Dog类创建了一个self,但是没初始化值,所以回父类,init,然后接着 调用 self.func(),此时调用的是 Dog.fun(self) #当自己有就调用自己的,没有才会去找父类 def guard(self): print('guarding') def func(self): print('Dog.func') dog = Dog() # class Bird(Animal): # def __init__(self,name): # self.name = name # def lay(self): # print('laying') # # b = Bird('jin') # print(b.name)
#关于组合和继承 #组合是 有,比如 老师类里有生日类 #继承是 是,子类是父类的关系,比如 dog是动物 #判断是 有 还是 是,来使用组合还是继承,还可以判断是否有重复代码,有则是继承 class Animal: def __init__(self,name,aggr,hp): self.name = name self.aggr = aggr self.hp = hp def eat(self): print('吃药回血') self.hp+=100 class Dog(Animal): def __init__(self,name,hp,aggr,kind): Animal.__init__(self.name,aggr,hp) #此处的self,传的是 Dog类的self,这样就可以用Animal来把name,aggr,hp,初识给Dog类的self self.kind = kind #此时即为派生属性 def eat(self): Animal.eat(self) #若子类方法和父类方法同名,既想实现父类的方法也想实现子类的方法,则需要在此调用父类的方法,传入的self是 子类本身的 self.teeth = 2 def bite(self, person): #派生方法 person.hp -= self.aggr print('%s被咬了,掉了%s点血,剩下%s生命值'%(person.name,self.aggr,person.hp)) class Person(Animal): def __init__(self,name,hp,aggr,sex): Animal.__init__(self.name, aggr, hp) self.name = name self.hp = hp self.aggr = aggr self.sex = sex def attack(self,dog): #把狗的实例化传进来 dog.hp -= self.aggr print('%s被打了,掉了%s点血,剩下%s生命值'%(dog.name,self.aggr,dog.hp)) # 父类中没有的属性 在子类中出现 叫做派生属性 # 父类中没有的方法 在子类中出现 叫做派生方法 # 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错 # 如果父类 子类都有 用子类的 #但是如果非要用父类的方法,而自己子类已经用同名方法,则需要在子类的方法中调用 Animal.eat(self) 此处的self传的是子类的self
#super 是python3中才有的方法 class Animal: def __init__(self,name,aggr,hp): self.name = name self.aggr = aggr self.hp = hp def eat(self): print('在animal中,回血了') class Dog(Animal): def __init__(self,name,aggr,hp,kind): #Animal.__init__(self,name,aggr,hp) #注意此处参数顺序,对于直接用父类进行init,参数顺序和子类一样即可 # 对于用super进行init,子类参数顺序一定要和父类一样,会以父类的参数顺序进行实例化,因为是父类在帮忙实例化Dog的self super().__init__(name,aggr,hp) #super(Dog, self).__init__(name,aggr,hp) 括号中的 Dog类和self可以省略 self.kind = kind #派生属性顺序只要按子类传入顺序即可 def eat(self): #Animal.eat(self) #如果子类中有同名的方法,子类会优先调用自己的方法,如果自己没有,才返回去找父类要 super().eat() #两种方式调取父类中的方法 print('在Dog类中') jin = Dog('金老板',100,500,'teddy') print(jin.__dict__) jin.eat() #此处如果 super(Dog,jin).eat() #super在类外面调用,就要传 dog类和对应的对象
#有道词典0909笔记 # 新式类有个原则,就是所有点都要找到,如果保证这个点能找到,广度优先(钻石问题,小乌龟问题) # python3中所有的类都是 新式类,都采用广度优先,有个方法可以查看继承顺序 print(B.mro()) # 在python2中的经典类,则时深度优先,一条路走到黑,走过的路不会走,然后换另一条路 # python2中的新式类和经典类,可以看下面的文章 # https://www.cnblogs.com/summer-cool/p/3884526.html #日常工作中主要还是使用单继承,多继承多用在设计模式中,及面试 # class F: # def func(self): print('F') # class A(F):pass # # def func(self): print('A') # class B(A): # pass # # def func(self): print('B') # class E(F):pass # # def func(self): print('E') # class C(E): # pass # # def func(self): print('C') # # class D(B,C): # pass # # def func(self):print('D') # # d = D() # # d.func() # print(D.mro()) # 新式类中的继承顺序 : 广度优先 class A(object): #著名的钻石问题 def func(self): print('A') class B(A): def func(self): super().func() print('B') class C(A): def func(self): super().func() print('C') class D(B,C): def func(self): super().func() print('D') b = D() b.func() #>>> 打印结果为 A C B D print(B.mro()) #2.7 # 新式类 继承object类的才是新式类 广度优先 # 经典类 如果你直接创建一个类在2.7中就是经典类 深度优先 # print(D.mro()) # D.mro() # 单继承 : 子类有的用子类 子类没有用父类 # 多继承中,我们子类的对象调用一个方法,默认是就近原则,找的顺序是什么? # 经典类中 深度优先 # 新式类中 广度优先 # python2.7 新式类和经典类共存,新式类要继承object # python3 只有新式类,默认继承object # 经典类和新式类还有一个区别 mro方法只在新式类中存在 # super 只在python3中存在 # super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的 # 继续写作业 : # 学生管理系统 登录,识别身份 进阶 : 不同视图不同的菜单
#多继承主要涉及到继承顺序,在新式类中是广度优先,经典类中是深度优先,有个查看继承顺序的方法 claaaname.mro()
继承特性中的两大设计模式:接口类和抽象类。褒贬不一,重视程度不一
#接口类和抽象类都不能实例化 # java : 面向对象编程 # 设计模式 —— 接口 # 接口类 : python原生不支持,满足接口隔离原则 #接口类支持多继承 #我要实现不同的功能,我就要去实现不同的接口,来继承这些功能。同时我要规范不同的接口要有哪些方法。 #规范的时候不实现这些方法,只在子类里实现 # 抽象类 : python原生支持的 from abc import abstractmethod,ABCMeta class Payment(metaclass=ABCMeta): # 元类 默认的元类 type @abstractmethod #检测子类的代码是否有相同的方法 def pay(self,money):pass # 没有实现这个方法 @abstractmethod def func2(self):pass #可以在这个规范中定义很多方法,这些方法只提供方法名,但是完全不用去实现。 #然后以这个方法作为父类,当我们写类方法的时候,如果遗漏了某个方法,或拼写错误,就会及时报错 # 规范 :接口类或者抽象类都可以 # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java class Wechat(Payment): def pay(self,money): print('已经用微信支付了%s元'%money) class Alipay(Payment): def pay(self,money): print('已经用支付宝支付了%s元' % money) class Applepay(Payment): def pay(self,money): #当如果这里的方法不叫pay,叫做fuqian,那么最上面的规范,就会报错,告诉你没调用到pay方法 print('已经用applepay支付了%s元' % money) def pay(pay_obj,money): # 统一支付入口 pay_obj.pay(money) # wechat = Wechat() # ali = Alipay() app = Applepay() # wechat.pay(100) # ali.pay(200) p = Payment()
#规范 #这个例子是典形的 接口类 做的事情 #接口类 #我要实现不同的功能,我就要去实现不同的接口,来继承这些功能。同时我要规范不同的接口要有哪些方法。 #规范的时候不实现这些方法,只在子类里实现 # tiger 走路 游泳 # swan 走路 游泳 飞 # oldying 走路 飞 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 # 接口类 刚好满足接口隔离原则 面向对象开发的思想 规范
#接口类 : python原生不支持,满足接口隔离原则 # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java # 抽象类 : python原生支持的 # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java #抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化 #如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 #在接口类中,通过规范不同的接口,最后各个子类,去继承这些接口类(多继承),来达到实现功能的目的,接口类的规范,使得子类不会遗漏功能和拼写错误 #在抽象类中,通过规范抽象出来的父类,当子类继承的时候(单继承),必须包含抽象类(父类)的功能,从而规范了子类 #抽象类中,子类方法一定要包含父类方法,只能多不能少 #一切皆文件 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) #为什么接口类中不能实现简单功能 # 抽象类 : 规范 # 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现 # 多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中 # 抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化 # java : # java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题 # 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题 # python # python中没有接口类 : # python中自带多继承 所以我们直接用class来实现了接口类 # python中支持抽象类 : 一般情况下 单继承 不能实例化 # 且可以实现python代码
# java : 面向对象编程 # 设计模式 —— 接口 # 接口类 : python原生不支持,满足接口隔离原则,不能被实例化,接口类中的所有的方法都必须不能实现(java) #接口类支持多继承 #我要实现不同的功能,我就要去实现不同的接口,来继承这些功能。同时我要规范不同的接口要有哪些方法。 #规范的时候不实现这些方法,只在子类里实现 # 抽象类 : python原生支持的 # 抽象类 不支持多继承(或者说一般都是单继承),抽象类中方法可以有一些代码的实现 —— java #抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化 #如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 #功能概括 #在接口类中,通过规范不同的接口,最后各个子类,去继承这些接口类(多继承),来达到实现功能的目的,接口类的规范,使得子类不会遗漏功能和拼写错误 #在抽象类中,通过规范抽象出来的父类,当子类继承的时候(单继承),必须包含抽象类(父类)的功能,从而规范了子类 #抽象类中,子类方法一定要包含父类方法,只能多不能少 #接口类和抽象类都不能实例化 #接口类和抽象类,这两种最主要的还是规范了固定的功能,就是这些规范了的方法一定要实现。且具体的功能和子类自己本身的功能还是要在子类中实现 #面试两句话:这两种都是规范,python中没有接口类 #为什么接口类中不能实现简单功能 # 抽象类 : 规范 # 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现 # 多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中 # 多态 python 天生支持多态 # 接口类和抽象类 在python当中的应用点并不是非常必要,不崇尚这两类设计模式 #ptyhon中崇尚的是鸭子类型(模仿,一个跟一个),比如list和tuple,有好几个相似方法,也没有采用继承来得到 #在其他静态语言类型中java等,要给函数传值,必须要先定义数据类型,所以在归一化设计的时候,类作为变量传入的时候,需要提供一个父类,作为数据类型定义,再传入 #此时这个父类,空着也是空着,干脆做一些规范(接口类和抽象类),一举两得 # java : # java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题 # 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题 # python: # python中没有接口类 : # python中自带多继承 所以我们直接用class来实现了接口类 # python中支持抽象类 : 一般情况下 单继承 不能实例化 # 且可以实现python代码 #python中抽象类和接口类本身很相似,因为这种设计模式,是从java中学习过来的,有些公司推崇,有些没有推崇 #python本身就支持多继承,所以没有接口类。 #所以受java影响,一般接口类用于多继承,抽象类用于单继承
二.多态
python天生支持多态
# 多态 python 天生支持多态 # 接口类和抽象类 在python当中的应用点并不是非常必要,不崇尚这两类设计模式 #ptyhon中崇尚的是鸭子类型(模仿,一个跟一个),比如list和tuple,有好几个相似方法,也没有采用继承来得到 #在其他静态语言类型中java等,要给函数传值,必须要先定义数据类型,所以在归一化设计的时候,类作为变量传入的时候,需要提供一个父类,作为数据类型定义,再传入 #此时这个父类,空着也是空着,干脆做一些规范(接口类和抽象类),一举两得 # def func(int num,str name): # pass # # func('alex',2) # class Payment:pass # class Alipay(): # def pay(self,money): # print('已经用支付宝支付了%s元' % money) # # class Applepay(): # def pay(self,money): # print('已经用applepay支付了%s元' % money) # # def pay(pay_obj,money): # 统一支付入口 归一化设计 # pay_obj.pay(money) # # pay() # 什么是多态 # python 动态强类型的语言 # 鸭子类型 # list tuple # 不崇尚根据继承所得来的相似 # 我只是自己实现我自己的代码就可以了。 # 如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 # list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的 # 优点 : 松耦合 每个相似的类之间都没有影响 # 缺点 : 太随意了,只能靠自觉 class List(): def __len__(self):pass class Tuple(): def __len__(self):pass def len(obj): return obj.__len__() l = Tuple() len(l) # 强类型语言 多态 # python 语言 鸭子类型 # 接口类和抽象类 在python当中的应用点并不是非常必要
三.封装
重要知识点
#重要知识点 # 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种 # 只让自己的对象能调用自己类中的方法 # 狭义上的封装 —— 面向对象的三大特性之一 # 属性 和 方法都藏起来 不让你看见 class Person: __key = 123 # 私有静态属性 def __init__(self,name,passwd): self.name = name self.__passwd = passwd # 私有属性 def __get_pwd(self): # 私有方法 return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名 def login(self): # 正常的方法调用私有的方法 return self.__get_pwd() alex = Person('alex','alex3714') print(alex.__dict__) #设置私有变量,其实都是在变量名的基础上加 _Person print(Person.__dict__) print(alex._Person__get_pwd()) # print(alex._Person__passwd) # _类名__属性名 # print(alex.login()) # 所有的私有 都是在变量的左边加上双下划綫 # 对象的私有属性 # 类中的私有方法 # 类中的静态私有属性 # 所有的私有的 都不能在类的外部使用
#在C++语言中为了保密,将所有的类属性都设置为私有 #父类中的私有属性,不能被子类调用 #总结 #会用到私有属性的场景 #1.隐藏起一个属性,不想让类的外部调用 #2.保护一个属性,不想让属性随意被改动 #3.保护一个属性,不想让子类调用
# 装饰器: @property @func.setter(可以传唯一一个参数) #func.deleter(与del配合使用) # from math import pi # print(pi) # # class Circle: # def __init__(self,r): # self.r = r #为什么不把面积写在属性里 self.area = pi*self.r**2。因为代码属性规范,面积不能直接改,只能用半径改。 # # @property # def perimeter(self): # return pi*self.r*2 # # @property # def area(self): # return pi*self.r**2 # # c = Circle(5) # print(c.area) # print(c.perimeter ) class Goods: discount = 0.5 def __init__(self,name,price): self.__name = name self.price = price #想调用私有属性 @property #加了property后可以让在类外调用函数,像调用属性一样,即免去加括号。但是此处就不能传参了 def name(self): return self.__name+'没过期' @name.setter #在有 @property装饰的name后,如果想修改私有属性,可以继续调用装饰器 函数名.setter def name(self,new_name): #然后定义一个一模一样的函数,参数为传入想改的值。 self.__name = new_name @property def get_price(self): return self.price * Goods.discount # apple = Goods('apple',5) # print(apple.name) # apple.name = '香蕉' # print(apple.name) # print(apple.get_price) class Person: def __init__(self,name): self.__name = name @property def name(self): return self.__name @name.deleter #这个deleter只有和调用的时候 del p.name 相作用才会实现函数功能 def name(self): del self.__name p = Person('erge') print(p.name) del p.name #这个del啥都没做,只是执行了name方法,且deleter只有和调用的时候 del p.name 相作用才会实现函数功能 print(p.name) #我不能用对象去删除一个方法,没权力
# staticmathod 静态的方法 *** # classmethod 类方法 **** # 类的操作行为 # class Goods: # __discount = 0.8 # def __init__(self,name,price): # self.name = name # self.__price = price # @property # def price(self): # return self.__price * Goods.__discount # @classmethod # 把一个方法 变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象 # def change_discount(cls,new_discount): # 修改折扣 # cls.__discount = new_discount # apple = Goods('苹果',5) # print(apple.price) # Goods.change_discount(0.5) # Goods.change_discount(Goods) # print(apple.price) # 当这个方法的操作只涉及【静态属性】的时候 就应该使用classmethod来装饰这个方法 # java class Login: def __init__(self,name,password): self.name = name self.pwd = password def login(self):pass @staticmethod def get_usr_pwd(): # # 静态方法 没有默认的参数 就象函数一样 ,当然也可以传参数 usr = input('用户名 :') pwd = input('密码 :') Login(usr,pwd) # Login.get_usr_pwd() getattr(Login,'get_usr_pwd')() # 在完全面向对象的程序中, # 如果一个函数 既和类没有关系,也和对象没有关系,就用staticmethod将它变为一个静态方法,随时都能调用 # 类方法和静态方法 都是类调用的 # 对象可以调用类方法和静态方法么? 可以 一般情况下 推荐用类名调用 # 类方法 有一个默认参数 cls 代表这个类 cls # 静态方法 没有默认的参数 就象函数一样 # 面向对象的进阶 # 网络编程
day59 Django 封装mypage这个功能,从app01中的views的函数,封装为mypage作为一个库来使用
补充:
class Foo: def __init__(self,name,age): self.name = name self.age = age def walk(self): print("%s正在散步"%self.name) return self #需要返回self def eating(self): print("%s正在吃饭"%self.name) f1 = Foo("gkx",22) f1.walk().eating() #在walk方法中return self,然后就可以进行链式操作了 # f1.eating()
class Foo: def func(self): #当类中没有 __init__ 也可以实例化,但是就没有办法给实例传属性了 #不能初始化self,此处的self只是一个普通参数,接收的是一个 空的实例 print('no init') f1 = Foo() f1.func() #不把参数传入self class Person: def __init__(self,name,age): self.name = name #self就是对象。可以把self看成一个字典,self后面的参数,是它的key, 类名()这里括号里的参数,传进来后是它的value age = age+1 #类的实例化默认是执行__init__函数,如果__init__的参数,不传入self,那么只能在__init__中使用,类中的方法调用不到 print(age) #换句话说,如果一个参数在__init__使用完后就没用了,就可以不用把它传入 self def name_(self): print("你叫什么",self.name) p = Person("gkx",22) #self.page_start = page_start # 当page_start判断完毕后,才传入self。对于那些直接在__init__一开始就传入self的来说,实例化的属性就是传入的值,不存在需要判断的,所以可以直接传入 #self.page_end = page_end # 但是此时page_start和page_end需要传入参数后,再做一定的判断才能确定下来,确定后才能传给self,方便方法中调用
#同时传入self的参数也不一定是要通过实例化传入的,也可以是经过实例化传入的参数,通过实例化传入的参数根据条件进行演变 而来的
py3.7新特性