面向对象

面向对象

初识面向对象

面向对象

什么都是对象,1是对象 ‘str’是对象 方法是对象 类也是对象,对象是一种体现,面向对象是一种思想。可以理解成:面向过程变成是在不停的搬砖,而面向对象编程是指点江山(分配任务),并不是一味的考虑怎么做,而是考虑如何分配。

面向对象,你就是上帝,上帝说有光,这个类就有光了,你要分配什么时候该有光,什么时候该黑暗,这就是对象的交互,产生结果

class World:
    def __init__(self)
        self.light = False

    def brighten(self):
        self.light = True

上面的class 关键字就是用来定义类的。注意:python中类的命名规范:大驼峰命名

在python3.x中,类有两种写法

class World:
  pass

class World(object):
  pass

利用缩进来规定代码块,是python一贯的作风,所以,class关键字下同一缩进代表着该类的内容

实例化

什么是实例化?

可以简单的理解为:实例化是一个过程,实例化得到的实例叫做对象

w = World()

实例化对象时,会自动调用 _ init _() 方法.

class World:
    def __init__(self, name):
        self.name = name

__init__()方法是用来初始化对象的。 初始化对象就是指:你这个类实例化成的对象进行添加初始化属性
对于这个World类的实例化如下:
w = World('Night') # 这里的'Night' 对应的name  是必须传递的,当 实例化对象时 ,所传递的参数必须要和 __init__()方法中的参数对应

别好奇 第一个self 参数是指对象本身,这个参数并不需要我们进行传递。
# 类名() 首先 会创造出一个对象,创建了一个self变量
    # 调用init方法,类名括号里的参数会被这里接收
    # 执行init方法
    # 返回self
对象属性
class World:
  def __init__(self, name):
    self.name = name

w = World('Night')
w.name

这里的w 是一个World类的对象, 而调用的 w.name 就是对象属性,对象属性顾名思义,一个对象可调用的属性

类属性
class World:
  pro = 'Obj'
  def __init__(self, name):
    self.name = name

print(World.pro)

在一个类中,没有写到方法中的属性,并且可以直接通过类名调用的属性称为类属性,例如 pro 。

注意: 对象可以调用类属性,而类不可调用对象属性

每次实例化对象时,都会开辟一块命名空间,对象的命名空间存放着指向类的指针,如果一个属性在对象中没有,则会通过指针找本类中的属性。 所以对象可以调用类属性
对象方法
class World:
    def __init__(self, name):
        self.name = name

    def func(self):
        print(self.name)
w = World('Night')
w.func()

通过对象调用的方法,有一个很明显的特征就是在定义方法时 第一个参数是 self

对象方法并不是只能通过对象调用, 还有一个比较神经病的方法
w = World('Night')
World.func(w)
通过类直接调用方法,并把对象当作参数传给self

类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

而类有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法
类的静态属性是共享给所有对象的
class World:
  pro = 'Obj'
  def __init__(self, name):
    self.name = name

print(id(World.pro))    # 19413504
w = World('Night')
print(id(w.pro))        # 19413504
类的动态属性是绑定到所有对象的
class World:
  pro = 'Obj'
  def __init__(self, name):
    self.name = name

  def func(self):
      print('asd')

w = World('Night')
print(World.func)   # <function World.func at 0x00B02588>
print(w.func)       # <bound method World.func of <__main__.World object at 0x00A9F930>>

组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

组合是一种 有 的关系 xxx 有 xx 就将 xx对象当作属性传给 xxx

圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用

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()) #计算环形的面积

三大特性

继承

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

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

在python3中所有类均继承object,就像最开始说 类有两种创建方式 ,其实都是一样的

如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如str)的实现。

>>> ParentClass1.__bases__
(<class 'object'>,)
>>> ParentClass2.__bases__
(<class 'object'>,)
单继承
伪代码如下:
class 动物:

    def(self):
        # do something

    def(self):
        # do something

    def(self):
        # do something

    def(self):
        # do something

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class(动物)def 喵喵叫(self):
        print '喵喵叫'

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class(动物)def 汪汪叫(self):
        print '汪汪叫'

单继承就是一个类继承一个父类,子类中可以调用父类的方法。也可以重写父类的方法,还可以派生其他方法

什么是派生?
派生方法、派生属性, 均指 子类中实现父类没有实现的方法 或 属性
使用super
子类调用父类的方法 
class Animal():
    def __init__(self,name):
        self.name = name

class Dog(Animal):
    def __init__(self,name,age):
        super().__init__(name)
        self.age = age

d =Dog('asd',1)
print(d.name)
print(d.age)
多继承
class Flay:
    def flay(self):
        print('我会飞')

class Run:
    def run(self):
        print('我会跑')

class Swim:
    def swim(self):
        print('我会游泳')

class Duck(Flay, Run, Swim):
    def __init__(self, name):
        self.name = name
        print('我的名字是{},我是一个鸭子'.format(self.name))
        self.flay()
        self.run()
        self.swim()


d = Duck('Duck1')

我的名字是Duck1,我是一个鸭子
我会飞
我会跑
我会游泳

多继承就是一个类继承多个父类。在python中通常情况下并不推荐使用多继承

继承的作用
继承有两种用途:

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

二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
抽象类 和 接口

继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

依赖倒置原则:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

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

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

twisted的twisted\internet\interface.py里使用zope.interface

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

设计模式:https://github.com/faif/python-patterns
什么是抽象类
  • 与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
  • 如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类是从一堆中抽取相同的内容而来的,内容包括数据属性和函数属性。
  • 比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西
  • 从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
  • 从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

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)
抽象类与接口的区别
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计 

在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口

接口隔离原则:
使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。

在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
多继承时的顺序
经典类

经典类是指不指明继承object 的类,但在python3中 所有的类均为新式类

经典类的继承顺序是——深度优先

新式类

新式类的继承顺序是——广度优先

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会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
警告
MRO是新式类的方法
supper 是python3中才有的方法

多态

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

多态性
...
peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()

python 是一个多态语言,天生支持多态

鸭子类型

序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系

可以理解成: 鸭子类型是 python中对多态的一种应用

封装

隐藏对象的属性和实现细节,仅对外提供公共访问方式。

原则
  1. 将不需要对外提供的内容都隐藏起来
  2. 把属性都隐藏,提供公共方法对其访问
私有化

在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的形式访问到.

#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
  1. 类中定义的__ x只能在内部使用,如self.__x,引用的就是变形的结果。
  2. __ 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
  3. 在子类定义的 _ _ x不会覆盖在父类定义的 _ _ x,因为子类中变形成了:_ 子类名 _ _ x,而父类中变形成了:_ 父类名 __x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
私有方法
#正常情况
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B


#把fa定义成私有的,即__fa
>>> class A:
...     def __fa(self): #在定义时就变形为_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A
property

属性方法

class Goods:

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)
del obj.price     # 删除商品原价

注意 @property所装饰的所有方法名必须相同

classmethod

类方法

class Classmethod_Demo():
    role = 'dog'
    @classmethod
    def func(cls): # 类方法  需要传递一个参数是当前类 不过和self一样,不需要手动传递
        print(cls.role)

Classmethod_Demo.func()
staticmethod

静态方法

class Staticmethod_Demo():
    role = 'dog'
    @staticmethod
    def func():     # 和普通的函数一样,不需要传什么self 或者 cls
        print("当普通方法用")

Staticmethod_Demo.func()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值