day6--面向对象初识

一面向过程与面向对象

面向过程:

流水线式的思维,顺着流程进行下去,类似于代码的堆叠,重视步骤

优点:不需要考虑太多东西,想到一个功能就写一个功能,堆叠代码

缺点:过程往往是先后执行的,要想变换功能或者增加功能,就要考虑重新堆叠大部分或者所有代码了,牵一发而动全身

面向对象:

将事务分解成各个对象,可以给这个对象附加相应的功能,后面的东西一旦调用,这些功能或者方法也就自然的拥有了

优点:对象可扩展,并可以单独修改,调用的地方会立刻显现,我是一块砖哪里需要哪里搬

缺点:无法预测出现问题的具体地方,对象和对象之间融合到一块了,找出问题需要很长时间

面向对象在实际运用中还是很普遍的,将事务拆分成各个对象,大家可以分头去做,往往进度比较快

面向对象的实现需要类和对象

二 类和对象

类:相当于一个模板,里面有这个模板的函数,变量,属性,方式方法等

对象:根据模板来创建,这样自然而然对象就拥有了类里面的东西

2.1 定义一个类

一个类的定义有其独特的方式,类名首字母大写,里面定义的函数,其参数必须有self

例如:

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")
print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

2.2实例化

类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

例如:

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;
        
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

实例化的过程就是类——>对象的过程

2.3类的属性

dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)
View Code

2.4类的查看

类的两种变量:静态变量和动态变量

例如:

class Person:  # class 关键字,定义了一个类
    '''
    类里面的所有内容
    '''
    animal = '高级动物' # 静态变量
    soup = '有思想'  # 静态变量

    def __init__(self,name,sex,eye,high,weight,):  # 构造方法

        self.eye = eye  # 属性
        self.name = name
        self.sex = sex
        self.high = high
        self.weight = weight
        print(666)

    def work(self): # 动态变量,动态方法,方法
        print(self)
        # self.job = 'IT'
        print('人会工作....')
View Code

1,类名.__dict__方法  只能查看,不能增删改。

# print(Person.__dict__)
# print(Person.__dict__['animal'])

#查询动态变量

 print(Person.__dict__['work'](11))

2 类名.变量名  可增删改查

# print(Person.animal)
# print(Person.soup)
# Person.kind = '有性格'
# Person.animal = '低等动物'
# del Person.kind

查询动态变量

#Person.work(11)

2.5对象的查看

实例化一个对象内部进行了三步

# 内部进行三步:
# 1,实例化一个对象,在内存中产生一个对象空间。
# 2,自动执行init方法,并将这个空间对象。 <__main__.Person object at 0x0000000001F5ABE0> 传给self
# 3,通过构造方法里的代码给空间对象添加一些属性,并返回给对象。
View Code

对象的查看与类的查看类似,只是将类名换成了对象名

类的静态属性是共享给对象的,类的动态属性是绑定给对象的

三面向对象的三大特性

3.1继承

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

继承简单的来说就是子继承父

继承分为:单继承和多继承

一些对象具体相同的功能时,可以在它们的父类中实现,这样的话可以减少很多代码

注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。

查看继承

类名.__bases__    #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类

如果没有指定基类,python会默认继承object类,object是所有python类的基类


例如:leiming.__bases__  

#结果为 (<class 'object'>,)

有很多情况是一个子类往往有很多父类,这个是多继承,它又是如何继承的

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

  • 当类是经典类时,多继承情况下,会按照深度优先方式查找
  • 当类是新式类时,多继承情况下,会按照广度优先方式查找

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

class D:

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
经典类多继承
class D(object):

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
新式类多继承

抽象类与接口类

继承有两种用途:

一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)

二:声明某个子类兼容于基类,定义一个接口类interface,接口类中定义了一些接口名(即函数)且并未实现

接口的功能,子类继承接口类,并且实现接口中的功能

这第二种就是接口继承

接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,

可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,

所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,

当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

为什么用接口?

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。

再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样

为什么用接口
View Code

在python中根本就没有一个叫做interface的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:

http://pypi.python.org/pypi/zope.interface

twisted的twistedinternetinterface.py里使用zope.interface

文档https://zopeinterface.readthedocs.io/en/latest/

设计模式:https://github.com/faif/python-patterns

 抽象类

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

为什么要有抽象类?

抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性

例如下面的例子

from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):    # 抽象类 接口类  规范和约束  metaclass指定的是一个元类
    @abstractmethod
    def pay(self):pass  # 抽象方法,python从语法上支持的规范类
 
class Alipay(Payment):
    def pay(self,money):
        print('使用支付宝支付了%s元'%money)
 
class QQpay(Payment):
    def pay(self,money):
        print('使用qq支付了%s元'%money)
 
class Wechatpay(Payment):  # 继承了Payment类,就必须去实现被abstractmethod装饰的方法
    def fuqian(self,money):
        print('使用微信支付了%s元' % money)
 
w = Wechatpay()  # 实例化
View Code

Payment不需要pay的代码,只需要子类实现即可

Payment没有任何代码实现,必须要求子类有同名的方法名

总之:抽象类和接口类做的事情:建立规范

依赖倒置原则:

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。换言之,

要依赖接口编程,而不是针对实现编程

如上例:Payment是高层模块,Wechatpay是底层模块。Wechatpay只能依赖Payment,Payment不能依赖Wechatpay

Payment是抽象类,它定义了pay方法,具体实现的逻辑,它不需要写,有子类完成。

 接口类

接口类:在 java 里面有interface的概念,它是一组功能的集合,接口的功能用于交互,所有的功能都是pubiic,即别的对象可操作

  接口只定义函数,但不涉及函数实现,并且它的这些功能都是相关的。

  但是在python中没有接口这样的关键字,但是可以实现类似的功能,它可以定义一个父类或者是基类,让它的子类强制使用相同函数名的方法

接口类:定义一个接口对继承类进行约束,接口里面有什么方法,继承类就必须有什么方法,接口中不能有任何功能代码

例如

from abc import ABCMeta,abstractmethod
class FlyAnimal(metaclass=ABCMeta):  # 飞行
    @abstractmethod
    def fly(self):pass
    @abstractmethod
    def cal_flying_speed(self):pass
    @abstractmethod
    def cal_flying_height(self):pass
class WalkAnimal(metaclass=ABCMeta):  # 走路
    @abstractmethod
    def walk(self):pass
class SwimAnimal(metaclass=ABCMeta):  # 游泳
    @abstractmethod
    def swim(self):pass
class Tiger(WalkAnimal,SwimAnimal):  # 老虎,继承走路和游泳
    def walk(self):pass  # 走路
    def swim(self):pass  # 游泳
class Monkey(WalkAnimal):  # 猴子
    def walk(self):pass
    def climb(self):pass  # 爬树
class Swan(FlyAnimal,WalkAnimal,SwimAnimal):  # 天鹅,继承飞行,走路,游泳
    def walk(self): pass
    def swim(self): pass
    def fly(self):pass  # 飞行
    def cal_flying_speed(self):pass  # 计算飞行速度
    def cal_flying_height(self):pass  # 计算飞行高度
class Parrot(FlyAnimal):  # 鹦鹉,继承飞行
    def fly(self):pass
    def cal_flying_speed(self): pass
    def cal_flying_height(self): pass
 
#实例化
Tiger()
Monkey()
Swan()
Parrot()
View Code

对于上面的例子,飞行的动物,陆地上面的东西,水中的动物都有各自的特点,不能使用一个抽象类限制其行为

接口隔离原则:使用多个专门的接口,而不使用单一的总接口。

那么接口类和抽象类该怎么说呢?

抽象类 是python中定义类的一种规范,用来约束子类中的方法的。被abstractmethod装饰的方法,子类必须实现,否则实例化时报错。

接口类 满足接口隔离原则,并且完成多继承的约束。如果不按照规范,在调用方法时,报错。

例如上面的例子,如果注释掉

# def cal_flying_height(self): pass
实例化:
k=Parrot()
调用方法:k.fly就会报错:TypeError: Can't instantiate abstract class Parrot with abstract methods cal_flying_height

3.2多态

多态是指一类事物有多种形态

class Payment:
    def pay(self):pass
 
class QQpay(Payment):
    def pay(self, money):
        print('使用qq支付了%s元'%money)
 
class Wechatpay(Payment):
    def pay(self, money):
        print('使用微信支付了%s元'%money)
 
def pay(pay_obj,money):  # 统一支付方法
    '''
    :param pay_obj: 实例化对象
    :param money: 金钱
    :return: 使用xx支付了xx元
    '''
    pay_obj.pay(money)  # 执行实例化对象的类方法pay
 
qq = QQpay()
we = Wechatpay()
 
pay(qq, 100)
pay(we, 200)
View Code

通过执行pay函数,传入不同的参数返回不同的结果,这就是多态

上面的例子可以进一步实现好的效果,将函数放到父类里面

class Payment:
    @staticmethod
    def pay(pay_obj,money): # 静态方法.需要通过类名+方法名来调用这个方法
        '''
        统一支付方法
        :param pay_obj: 实例化对象
        :param money: 金钱
        :return: 使用xx支付了xx元
        '''
        pay_obj.pay(money)
         
class QQpay(Payment):
    def pay(self, money):
        print('使用qq支付了%s元'%money)
 
class Wechatpay(Payment):
    def pay(self, money):
        print('使用微信支付了%s元'%money)
         
qq = QQpay()
we = Wechatpay()
 
Payment.pay(qq, 100)
Payment.pay(we, 200)
View Code

python中自带多态效果

对于object类,python2中是封闭的,但是在python3中是开放的,也就是可以被继承

鸭子类型

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

在Linux系统中,一切皆文件的概念就是一个典型的例子,任何东西都可以看作有读和写两种属性

鸭子类型:不是通过具体的继承关系来约束某些类中的必须有那些方法名

是通过一种约定俗成的概念,来保证在多个类中,相似的功能叫相同的名字

3.3封装

封装,顾名思义就是将内容封装到某个地方,以便于被其他对象调用

class Foo:

  def __init__(self,name,age):  #创建对象的时候会自动执行init函数

    self.name=name

    self.age=age

obj1=Foo('xingchen',18)  #将xingchen和18分别封装到obj1/self 的name和age属性中

self是一个形式参数,当执行obj1=Foo('xingchen',18)时,self等于obj1,内容被封装到了对象obj1中

调用时,可以通过对象直接调用被封装的内容,或者通过self间接调用被封装的内容

class Foo:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def detail(self):
        print(self.name)  #间接调用
        print(self.age)

obj1=Foo('xingchen',18)
obj1.detail() #直接调用
View Code

其他:扩展性

封装之后,内部的方法可以随时改变,并且可以保证你调用时使用的时同样的方法

原文地址:https://www.cnblogs.com/mmyy-blog/p/9223562.html