面向过程
优点是:极大的降低了程序的复杂度
缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
类是创建新对象类型的机制。
类定义了一组对象,这些属性与一组实例的对象相关,且由其共享
class Account(object):
num_account=0 #变量可以追踪存在多少个Account实例
def __init__(self):
something
num_account+=1
def __del__(self):
something
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
抽象类
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
多态性
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
多态性分为静态多态性和动态多态性
静态多态性:如任何类型都可以用运算符+进行运算
动态多态性:
>>> def func(animal): #参数animal就是对态性的体现 ... animal.talk() ... >>> people1=People() #产生一个人的对象 >>> pig1=Pig() #产生一个猪的对象 >>> dog1=Dog() #产生一个狗的对象 >>> func(people1) say hello >>> func(pig1) say aoao >>> func(dog1) say wangwang
封装
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
在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的形式访问到.
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了
静态方法
上述方法都在一个实例中操作,该实例总是做为第一个参数self传递。
如果不想在实例上操作调用类函数,可以使用静态方法(装饰器@staticmethod)
class Foo(object):
@staticmethod
def add(x,y):
return x+y
如果调用静态方法,只需要如下使用:
x=Foo.add(3,4) #x=7
类方法
类方法hi将类本身做为对象进行操作。类方法使用@classmethod装饰器定义,与实例防范呢不同,根据约定,类是作为第一个参数(cls)传递的。
class Data(object): @classmethod def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_month,t.tm_day) class EuroData(Data): something a=Data.now() #调用Data.now(Data)放回Data b=EuroData.now() #调用Data.now(EuroData)并返回EuroData
特性
特性(property)是一种特殊属性,访问它时会计算它的值
class Circle(object): def __init__(self,n): self.n=n @property def area(self): return math.pi*self.n**2 @property def perimeter(self): return 2*math.pi*self.n c=Circle(2) c.radius c.area c.perimeter #特性property装饰器以简单属性的形式去访问后面方法,而不需加()
-----------------------------------
class Foo(object): def __init__(self,name): self.__name=name @property #将属性name定义为只读特性 def name(self): return self.__name @name.setter def name(self,value): if not isinstance(value,str): raise TypeError("Must be a string") self.__name=value @name.deleter def name(self): raise TypeError("can't delete name") f=Foo("alex") n=f.name #调用f.name-get函数 print(f.name) f.name="xxxx" #调用setter name(f,"xxxx") print(f.name) # f.name=45 #调用setter name(f,45) ->TypeError # print(f.name) del f.name #调用delter name(f) ->TypeError
描述符
对属性的访问将由一系列用户定义的get,set,delete函数控制,这种方式可通过描述符对象进一步推广
类的特殊成员方法
__doc__ 表示类的描述信息
__module__ 表示当前在操作那个模块
__class__ 表示当前操作的对象的类是什么
__init__ 构造方法
__del__ 析构方法
__call__
__dict__ 查看类或对象中的所有成员
类是由type类实例化产生的 (类中的一个属性,__metaclass__,表示该类由谁来实例化创建,下面代码描述类实例化流程)
#类创建方法 type->new->init----实例化类对象---->call class MyType(type): def __init__(self,*args,**kwargs):#3 print("MyType __call__",self,*args,**kwargs) def __call__(self, *args, **kwargs):#5 print("MyType __call",self,*args,**kwargs) obj=self.__new__(self) self.__init__(obj,*args,**kwargs) obj.age=22 return obj def __new__(cls, *args, **kwargs):#2 print("MyType __new__",*args,**kwargs) obj=type.__new__(cls,*args,**kwargs) return obj class F00(object,metaclass=MyType):#1 def __init__(self,name): self.name=name print("Foo __init__") def __call__(self, *args, **kwargs): print("Foo __call__") f=F00("Alex")#4
反射
通过字符串映射或修改程序运行时的状态、属性、方法, 有以下4个方法
class Foo(object): def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' obj = Foo() # #### 检查是否含有成员 #### hasattr(obj, 'name') hasattr(obj, 'func') # #### 获取成员 #### getattr(obj, 'name') getattr(obj, 'func') # #### 设置成员 #### setattr(obj, 'age', 18) setattr(obj, 'show', lambda num: num + 1) # #### 删除成员 #### delattr(obj, 'name') delattr(obj, 'func')
反射用法:
class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr
#from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('处理其他的逻辑')
super用法
class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜单车 pass line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()