面相对象
----------------------------------------------------------------------------------------------------------------
定义:具有相同属性和方法的一类事物就是类,对象是类中一个具体的列子是对象\实例
由类创建对象的过程
那什么是类,什么是对象,什么是实例,什么是实例化?
类:具有相同属性和方法的一类事物(框架,模子) , 命名空间是方法和(静态属性\字段)
对象:对象是类中一个具体的例子(拥有具体的属性值和具体的方法),命名空间是类指针\对象的所有属性
实例:实例完全和对象一样
实例化:是由类创建对象的过程
类名能做什么?
1.实例化
2.调用类中的静态属性
实例化的过程:对象名==类名(参数)
1.创建一个属于对象的空间
2.将这个空间的地址和参数传递给__init__方法
3执行init方法
4.将对象的空间返回给对象名
对象能做什么?
1存储在实例化之后创建的空间中的所有变量都是对象的属性
2.每一次实例化产生的空间都是独立的,每一个对象都有自己的属性值
类有两个属性:静态属性和动态属性
类的两个作用:属性引用和实例化
属性引用(类名\属性)
class Person: #定义一个人类 role = 'person' #人的角色属性都是人 def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化
class A: role='人' def __init__(self,name): self.name=name def walk(self): print('我要飞得更高') a=A('路飞') #实例化一个对象 print(a.walk()) # 调用方法
静态属性:所有的这个类的对象都共有的属性
动态属性:定义在类中的函数,自带一个self,就是调用这个方法的对象本身.
from math import pi class Circle: ------类\类名 def__init__(self,r): ------对象\实例 self.r=r def zc (self): return 2*pi*self.r def mj (self): return pi*self.r**2 a=Circle(5) ----实例化 print(a.zc())
面向过程:
优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可.
缺点:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身
应用场景:一但完成基本很少改变的场景,著名的例子有Linux内核,git,以及Apache HTTP Server等
面向对象:
优点:解决了程序的扩展性.对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易
缺点:可控性差,无法像面向过程的程序设计l流水线式的可以很准确的预测问题的处理流程,面向对象的程序一旦开始就由对象之间的交互解决问题,即使是上帝也无法预测结果.
应用场景:需求经常变化的软件,一般需求的变化都集中在用户端,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方.
对象之间的交互:
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; def attack(self,dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.life_value -= self.aggressivity class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵称; self.breed = breed # 每一只狗都有自己的品种; self.aggressivity = aggressivity # 每一只狗都有自己的攻击力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 people.life_value -= self.aggressivity egg = Person('egon',10,1000) #创造了一个实实在在的人egg ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2 print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10点血
面向对象的组合用法:
1.教授的生日,有的关系
class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(self,name,gender,birth,course): self.name=name self.gender=gender self.birth=birth self.course=course def teach(self): print('teaching') p1=Teacher('egon','male', BirthDate('1995','1','27'), Couse('python','28000','4 months') ) print(p1.birth.year,p1.birth.month,p1.birth.day) print(p1.course.name,p1.course.price,p1.course.period) ''' 运行结果: 1 27 python 28000 4 months '''
第二种方法:用圆来求出圆环
from math import pi class Circle: ''' 定义了一个圆形类; 提供计算面积(area)和周长(perimeter)的方法 ''' def __init__(self,radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi *self.radius circle = Circle(10) #实例化一个圆 area1 = circle.area() #计算圆面积 per1 = circle.perimeter() #计算圆周长 print(area1,per1) #打印圆面积和周长 class Ring: ''' 定义了一个圆环类 提供圆环的面积和周长的方法 ''' def __init__(self,radius_outside,radius_inside): self.outsid_circle = Circle(radius_outside) self.inside_circle = Circle(radius_inside) def area(self): return self.outsid_circle.area() - self.inside_circle.area() def perimeter(self): return self.outsid_circle.perimeter() + self.inside_circle.perimeter() ring = Ring(10,5) #实例化一个环形 print(ring.perimeter()) #计算环形的周长 print(ring.area()) #计算环形的面积
import pickle class Course: def __init__(self,name,price): self.name=name self.price=price class Student: def __init__(self,name,price): self.name = name self.price = price python=Course('python',19800) with open('Courses','wb') as f: pickle.dump(python,f) with open('Courses','rb')as f: pickle.load(f) print(Course)
定义:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方法式去表达出抽象的结构
java:不支持多继承
python:支持多继承,通过抽象类的多继承来实现复杂的规范
继承的作用:
1)减少代码的重用
2)提高代码可读性
3)规范编码模式
继承有两种语法:
单继承和多继承
基础概念
父类: 超类\基类
子类:派生类
子类使用父类的方法:
1)子类中油,父类中没有:用子类
2)子类中没有,父类中有:用父类
3)子类\父类中都有:默认情况下用子类的||||既又想用父类,又想用子类: 1)父类方法.方法名(子类对象)
2)super().方法名()
4)都没有就报错
归一化设计-------------obj
class A: def eat()self): pass class B : def eat(self): pass def eat(obj): obj.eat()
定义:抽象类是一个特殊的类,他的特殊之处在于只能被继承,不能被实例化
抽象类主要就是作为基类/父类,来约束子类中必须实现的某些方法
特点:
---------1必须在类定义的时候指定metaclass=ABCMeta
----------2必须在要约束的方法上方加@abstractmethod 方法
from abc import ABCMeta,abstractmethod #(抽象方法) class Payment(metaclass=ABCMeta): # metaclass 元类 metaclass = ABCMeta表示Payment类是一个规范类 @abstractmethod # @abstractmethod表示下面一行中的pay方法是一个必须在子类中实现的方法 def pay(self):pass @abstractmethod def back(self):pass # 退款功能 class AliPay(Payment): def __init__(self,name): self.name = name def pay(self,money): # 支付宝提供了一个网络上的联系渠道 print('%s通过支付宝消费了%s元'%(self.name,money)) def back(self): print('退款') class WeChatPay(Payment): def __init__(self,name): self.name = name def pay(self,money): # 微信提供了一个网络上的联系渠道 print('%s通过微信消费了%s元'%(self.name,money)) def back(self): print('退款') class ApplePay(Payment): def __init__(self,name): self.name = name def pay(self,money): print('%s通过苹果支付消费了%s元'%(self.name,money)) def back(self): print('退款') # 归一化设计 : 从原来的面向对象编程 -->面向函数编程.降低了用户的使用成本 def pay_func(person,payway,money): if payway == 'alipay': per = AliPay(person) elif payway == 'wechatpay': per = WeChatPay(person) elif payway == 'ApplePay': per = ApplePay(person) per.pay(money) pay_func('lufei','alipay',200) pay_func('nazha','wechatpay',100)
定义:接口只是定义了一些方法,而没去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能
接口类: 其实跟抽象类几乎一样
1)python中由于有了抽象类的多继承,不需要接口的概念了
2)一个基类写出来被子类继承了:接口类
3)并且在方法中只写pass(你可以选择性的满足)
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
class Base: def __init__(self): self.func() def func(self): print('in hase') class Son(Base): def func(self): print('in Son') s=Son 结果: in Son
参数 抽象类 接口
默认的方法实现 它可以有默认的方法实现 接口完全是抽象的,根本不存在方法的实现
实现 子类使用extends关键字继承抽象类.如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法实现. 子类使用关键字implements实现接口.它需要提供接口中所有声明的方法实现
构造器 抽象类可以有构造器 接口不能有构造器
与正常JAVA类的区别 除了不能实例化抽象类之外,它和普通JAVA类没有任何区别 接口是完全不同的类型
访问修饰符 抽象方法可以有public、protected和default这些修饰符 接口方法默认修饰符是public,不可以是使用其他的修饰符
main方法 抽象方法可以有main方法,可以运行它 接口没有main方法,不能运行它
多继承 抽象方法可以继承一个类和实现多个接口 接口只可以继承一个或多个其他接口
速度 它比接口速度快 接口是稍微有点慢,需要时间去寻找在类中实现的方法
添加新方法 在抽象类中添加新的方法,可以给它提供默认的实现,不需要改变你现在的代码 在接口中添加方法,必须改变实现该接口的类
新式类:默认继承object类 ###object是所有类的父类
super:返回父类
PFP8规范:跨环境 无论在py2还是py3中定义一个新式类,都需要加上object这个继承
python2:有新式类和经典类
经典类:
不主动继承object类的所有类都是经典类
继承关系遵循深度优先算法
没有mro方法,没有super
新式类:
所有继承object类的所有类都是新式类
和python3一样继承遵循mro顺序和c3算法
有mro方法,但是super的使用必须传参数super(子类名,对象名)方法名
class A: def func(self):print('A') class B(A): def func(self): super(B, self).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 = B() b.func()
python3:只有新式类
新式类:
不需要主动继承object
py3 super()可以直接使用,而py2不行
class A: def func(self):print('A') class B(A): def func(self): # super(B, self).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 = B() b.func() print(B.__mro__)
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
定义:已经实现了'不同类之间的相同的方法都用同一个名字',解决不同类的对象的传参问题
python是一门再带多态的语言 ,处处是多态
在python中体现的多态:
几个类拥有同名的方法,可以写一个函数,来统一进行调用----归一化设计
java要求传递数据的数据类型?
是为了代码不出错误,只要你能够顺利的调用这个函数,那内部的执行就大大降低了出错率
python要不要求?
不要求,更多的不是硬性的规定,但是可能会因为传递的参数不符合规则而使得代码报错,但是dada降低了代码的繁琐程度
# python # class Cat(object): # def eat(self):pass # class Dog(object): # def eat(self):pass # def eat(obj): # obj.eat() # 小花 = Cat() # 小花就是猫类的对象 类==类型==数据类型,小花的类型就是Cat # 小黑 = Dog() # eat(小花) # eat(小黑) # 动物吃表现出来的多种状态: # 猫吃 # 狗吃
鸭子类型是python语言中特有的,不依赖于继承和规范来进行的一种约定(依赖继承和规范指).
定义:不需要解决类的对象的传参问题,就可以直接用,已经实现的'不同之间的相同的方法都用同一个名字'
什么是封装?
广义上:
-------把一类事物的相同的行为和属性归到一个类中
狭义上:
-----把一些特殊的属性和方法藏在类中
------外部无法使用,只有内部可以调用
class Dog: # dog_sum = 0 # 不安全,因为这个属性可以在类的外部被随便修改 __dog_sum = 0 # 安全,通过dog_sum方法和__变量名让属性变成只能看不能改的值 def __init__(self): self.count() def count(self): Dog.__dog_sum += 1 # 可以从一个类的内部使用__dog_sum的属性 def dog_sum(self): return Dog.__dog_sum print(Dog.__dict__) alex = Dog() print(Dog.__dict__) # print(Dog.__dog_sum) # 不可以从外部直接使用这个变量 print(alex.dog_sum())
写法:
只要是在类的内部的名字面前加上双下换线,那么这个名字就变成私有的(只能在类的内部使用,不能再类的外部使用)
在定义的时候,存储的名字就会发生变化_类名__名字
因此,在类的外部就不会被调用到了
在类的内部 使用__名字,默认就被改成_类名__名字
----------------------------------------------------------------------
私有静态属性:为了不随意被外部修改
私有方法:不希望被外部随意调用
私有的对象属性:
-----不希望某个值被随便修改
------不希望某个值被从类的外部看到
好处:
1)将变化隔离
2)便于使用
3)提高复用性
4)提高安全性
封装的原则
-------将不需要对外提供的内容都隐藏起来
-------把属性都隐藏,提供公共方法对其访问
定义:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能力(自省)
反射的四中功能:
hasattr / getattr // setattr //delattr
hasattr:检测是否含有某属性
getattr:获取属性 -----不存在,报错
setattr:设置属性
delattr:删除属性
class A: a='路飞' b='索隆' @staticmethod def B(): print('大傻逼') while 1: attr=input('>>>').strip() if attr.upper()=='Q':break if hasattr(A,attr): obj=getattr(A,attr) if callable(obj): obj() else: print(obj) else: print('错误')
class Foo: f = '类的静态变量' def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print('hi,%s'%self.name) obj=Foo('egon',73) #检测是否含有某属性 print(hasattr(obj,'name')) print(hasattr(obj,'say_hi')) #获取属性 n=getattr(obj,'name') print(n) func=getattr(obj,'say_hi') func() print(getattr(obj,'aaaaaaaa','不存在啊')) #报错 #设置属性 setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj)) #删除属性 delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_name111')#不存在,则报错 print(obj.__dict__)
1.不可以对某些计算结果直接赋值
2.一般是通过对计算结果产生影响的属性进行重新赋值
1.面向对象分析(object oriented analysis ,OOA) 软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。 建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。 2 面向对象设计(object oriented design,OOD) 根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。 首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。 在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述 3 面向对象编程(object oriented programming,OOP) 根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python 4 面向对象测试(object oriented test,OOT) 在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。 面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。 5 面向对象维护(object oriendted soft maintenance,OOSM) 正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。 由于使用了面向对象的方法开发程序,使用程序的维护比较容易。 因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。 在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。 现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。 在一个大型软件开发过程中,OOP只是很小的一个部分。 对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
抽象/实现 抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。 对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。 封装/接口 封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。 注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来” 真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明 (注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在) 合成 合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。 派生/继承/继承结构 派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。 继承描述了子类属性从祖先类继承这样一种方式 继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。 泛化/特化 基于继承 泛化表示所有子类与其父类及祖先类有一样的特点。 特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。 多态与多态性 多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气 多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。 冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样 自省/反射 自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__