python-面向对象

preface

话不多说,直接进入正文:
include:

  1. 编程思想
  2. 为啥要用类而不用函数呢
  3. 用代码去了解函数到类的演变
  4. 面向对象的编程
  5. 类的变量和实例的变量
  6. 类的三大特性
  7. 抽象类

编程思想

我个人所了解的有2种,还有个函数式编程的概念模模糊糊(大概是把常用的代码封装成一个函数,重复调用即可,先认为就是这样吧)

  1. 面向对象:面向对象是把所有的事务物当作独立的对象(建立对象对的目的不是为完成一个步骤,而是为了描述这个些对象在整个解决问题的步骤中的行为),处理问题过程中所思考的是各个对象之间的关系(JAVA,python)。

  2. 面向过程:根据事务的逻辑分析出解决问题所需要步骤,再用函数一步一步的去实现这些步骤,使用的时候一个个依次调用就行了。(C语言是典型的代表)

无论用什么形式来编程,我们都要明确记住以下原则:

  • 写重复代码是非常不好的低级行为
  • 你写的代码需要经常变更

面向对象和面向过程的资料参考:点我

为啥要用类而不用函数呢

  1. 记住两个原则:
    1.1.减少重复代码
    1.2. 代码会经常变更
  2. 会对变量或字符串的合法性检测(在实例初始化的时候能够统一初始化各个实例的变量,换做函数来说,要弄出同样的变量那么在初始化的时候必须一个一个的去写代码,容易写错。)
  3. 类能够隐藏属性或者变量。

用代码去了解函数到类的演变

简单了解了什么是面向对象和面向过程的编程思想后,那么就开始用代码去了解函数到类的演变。
简单的业务内容:对CPU,内存,磁盘使用率监控,使用率超过90%报警。
当我们刚接触python这门语言的时候,作为小白,根据这个业务逻辑肯定是挨个挨个的去判断(面向过程的思想),然后去发送报警信息,那么就有了下面的代码:

while True:
    if cpu.usage > 90:
            mail(CPU报警)    #(假设mail这里是一个发送邮件的功能,我们直接在括号内输入要发送的内容就行了)
    if mem.usage > 90:
            mail(内存报警)
    if disk.usage > 90:
            mail(磁盘报警)

但是随着知识的增长,此时我们学习了函数,了解到了函数式编程,那么可以把代码精简如下:

def judge_usage(name,content):        #把判断功能和发送功能做成一个函数,直接传参就行了
    if name > 90:
            mail(content)
    
judge_usage(cpu.usage,cpu报警)         #那么我们调用的时候直接传入内存,CPU,磁盘的使用率就行了。

好了到此为止我们觉得这个代码就行了,最起码把代码精简了,省去了重复的代码。
那么今天我们就来学习下 --> 面向对象的编程

面向对象的编程

说道面向对象,那么不得不提类和对象,那么在python里面什么类,什么是对象。

  • 类:一堆方法的集合,但是这些方法之间又有联系。
  • 对象:类实例化后的结果就是对象。

咱们也得知道,通过实例化后的对象可以执行类里面的一些方法。

代码例子:

#面对对象的编程  OOP
class Role(object):                                 #通过class这个关键字来创建一个类,Role表示这是类的名字,(object)是新式类的写法 必须这么写(以后再说原因),表示继承object类,3.0以上的版本最好都写上。
    def __init__(self,name,role,gun,life_value):    #__init__用函数的话说这里就是Role类的初始化函数,使用类的专业名词就是说构造方法,在类实例化的时候会默认执行。,这里的self表示为对象本身,而对象又是在实例化的时候创建的
        self.name=name                              #把name赋值给self.name
        self.role=role
        self.gun=gun
        self.life_val=life_value

    def buy_gun(self,gun_name):                     #定义了一个buy_gun的方法
        print("%s buy %s"%(self.name,gun_name))
        self.gun=gun_name

我们这样使用这个类:

#把一个抽象的类变成一个具体的对象的过程叫实例化
p1=Role('lisi','police','AK47',100)
print('P1 ',p1.buy_gun('xxx'))   # 其实上面那语句 p1=Role('lisi','police','AK47',100)  等价于这句   Role(p1,'lisi','police','AK47',100),p1的所在的位置就对应的是self。这么做的目的就在于把'lisi','police','AK47',100和p1关联起来

说清楚点就是:实际上我们执行上面实例化的语句时做了两件事情:

  1. 在内存开辟空间,指向p1这个变量名。
  2. 开始实例化了,调用了Role这个类,并且执行了 init 这个构造方法,相当于Role.__init__(p1,'lisi','police','AK47',100),注意看,p1占用了self的位置,为啥会这样,其实这么做的目的就在于把'lisi','police','AK47',100和刚才开辟的变量名p1关联起来,也许你就会问了,为啥要关联起来,如果不关联起来,那么这4个参数就没有跟谁了,解释器就会报错了。
  3. 所以说,p1=Role('lisi','police','AK47',100)这样一运行后,self.name = name ,self.role = role ...... 就把'lisi','police','AK47',100 这4个值放入了p1这个内存空间里面。

到此你也许会问为啥在第二个方法里面buy_gun还有个self呢?那么现在就解答你的问题。
虽然我们在实例化的时候把传入了那几个参数和p1关联起来了,但是我们在调用这个bug_gun方法的时候,需要告诉buy_gun,从谁那里获取信息,怎么去获取,于是把p1传给self,这样buy_gun才能够去访问p1的信息,所以能够打印出 self.name(p1.name)来,其实最简单的回答就是调用方法的时候,这个方法得知道who are you。do you understand?? i think you will answer i do!!

好了,说了那么多,我们总结下出以下2点:

  1. 上面的这个p1=Role('lisi','police','AK47',100)动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例
  2. 刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容。

类的变量和实例的变量

曾经面试的时候问道:类变量和实例的变量有什么区别?

class aa(object):
    x=0
    def __init__(self,name):
        self.name=name
    def buy_some(self,goods_name):
        print("%s buying %s"%(self.name,goods_name))
        #self.x=goods_name
        #print('on buy_some -->',self.x)

bb=aa('bb')
cc=aa('cc')
aa.name='AK47'
print('aa.weapon -->',aa.name)
dd=aa('lisi')
aa.x="AA's thing"
aa.c='ccccccccc'
aa.name='AA'
bb.x='first'
bb.c='xxxxxxx'
bb.buy_some('lumia1020')
print('bb.c-->',bb.c)
#print('bb name--',bb.name)
cc.buy_some('Nokia N1')
print('cc c -->',cc.c)

从上面的代码中可以观察到:

  1. 在类里面的初始化方法__init__(),只有在调用的时候才才会创建self.name这个变量,倘若没有调用,那么直接运行aa.name是会报错的,
    因为self.name是属于实例的变量(也叫成员属性,实例的属性),存在实例的内存中,不属于类的变量。
  2. 在类里面的变量x,在类没有被调用的也是存在的,存在类的内存里,所以aa.x是可以的。
  3. aa这个类里面的 buy_some 方法,他是在aa这个类的内存里面的,不是在实例的内存里面的,所以实例每次调用这个 buy_some 方法的时候,都得
    去类里面调用,并且告诉这个方法调用者的信息(其实就是实例的变量,buy_some第一个参数就是self,就是用来接受调用者信息的)。那会有人问了,为什么
    不在每个实例化的过程中把 buy_some 方法都添加到实例内存中的,这样的话buy_some方法就在实例内存中,就不用在调用buy_some这个方法的告诉这个方法调
    用者的信息,显然这个想法是错误的,为啥,因为函数对于外面是封闭的,所以调用buy_some的时候还得告诉调用者的信息,并且这样在每个实例中添加相同的
    方法,那样只会占据更多的内存。那又有人会问了,我们可以把全局变量在buy_some这个方法中写死,如刚才实例化成的bb,cc,但是这里有个问题是,写死的
    前提必须这个全局变量存在,但是没有实例化之前这个变量又是不存在的,所以这里是一个非常矛盾的问题。说了这么多就是想说明这点,类里面的方法除了构
    造方法之外,都在类的内存里面,调用类里面的方法的时候必须告诉这个方法 调用者的信息。

把上面3句话说精简点就是:

  1. 类的变量可以随时访问,实例的变量只有在实例化之后才能访问。
  2. 类里面的方法除了构造方法之外,都在类的内存里面,调用类里面的方法的时候必须告诉这个方法 调用者的信息。
  3. 倘若在类变量xx=None, 那么实例化之后,实例.xx='something'相当于把xx='something'放入了实例的内存里,如果直接实例.xx,就等于直接取类的变量。

类的三大特性

python 类的三大特性:

  1. 封装:就是把客观事物封装成抽象的类,而且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
  2. 继承:它可以使用现有类的所有功能,并且在无须重新编写原来的类的情况下对这些功能进行扩展.继承是从一般到特殊的过程,父类是定义一般的方法,而子类是有特殊的方法,所以这就是从一般到继承的过程。
  3. 多态:实现的目的在于对接口的重用。

类的继承

继承也是类的一大特性,它可以使用现有类的所有功能,并无需重新编写原来的类的情况下对这些功能进行扩展。在这里我们要了解两个概念 :

  1. 父类(也有人称超类,基类):被继承的类。
  2. 子类(也有人称派生类):通过继承创建的类。

继承的过程,也就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Compostion)来实现。
继承是从一般到特殊的过程:父类是定义一般的方法,而子类是有特殊的方法,所以这就是从一般到继承的过程。
一般情况下一个子类只有一个基类,要实现多重继承,可以通过多级继承来实现。 我们看如下代码:

class animal(object):
    def __init__(self,name):
    self.name=name
    
    def eat(self):
    print("%s eating!!"%self.name)
    
    def drink(self):
        print("%s drink!!"%self.name)
    def sleep(self):
        print("%s sleep!!"%self.name)
    def discharge(self):
        print("%s discharge!!"%self.name)

class people(animal):                #这里开始继承animal类
    def __init__(self,name,hair,thought):
        super(people,self).__init__(name)        #super 是用来解决多重继承问题的,咱们后面会讲到
        self.hair=hair
        self.thought=thought

    def whether_thought(self):
        print("He's name is %s ,and he can thought(%s) as people!!"%(self.name,self.thought))

class monkey(animal):
    def __init__(self,name,hair,climb):
        super(monkey,self).__init__(name)
        self.climb = climb
    def whether_climb(self):
        print("he is monkey and he's name is %s,he can climb(%s)"%(self.name,self.climb))

p1=people('ljf','black','yes')            
p1.whether_thought()                #p1可以调用属于自己的方法,但是无法调用monkey的whether_climb的方法
print(p1.hair)                    
p1.eat()                            #p1可以调用父类eat的方法

m1=monkey('wukong','yellow','can climb')
m1.whether_climb()
m1.drink()                        #m1可以调用父类的drink方法

python类的多态:

实现的目的在于对接口的重用。

#!/usr/bin/env python


class animal(object):  #定义一个父类
    
    hobbie='eat meat'   #定义一个类变量
    def __init__(self,name):
        self.name=name
    def talk(self):
        print('talking.......')


class dog(animal):  #定义一个子类
    def talk(self):
        #print('i am dog ,wangwangwang')
        return 'i am dog ,wangwangwang'


class cat(animal):   #定义一个子类,继承父类
    def talk(self):
        #print('miao miao miao')
        return 'miao miao miao'


def animal_talk(obj):  #定义一个函数,用来调用实例的方法,这样对外的话,我提供了一个接口来分别给使用者调用talk方法
    print(obj.talk())

d=dog('ljf')

d.hobbie='fish'  #定义实例的变量,变量名是hobbie,这个实例的hobbie不同于类的hobbie,所以在print的时候只会打印实例的hobbie,
print(d.hobbie)
c=cat('yq')

print(c.hobbie)  #这个实例c由于没有自己的类变量hobbie,所以在打印的时候就显示类的变量hobbie
animal_talk(d)   # 实现的目的在于对接口的重用。
animal_talk(c)   # 实现的目的在于对接口的重用。

抽象类

我们在父类中定义了一个方法
比如

class father(object):
   .....
    def ChouXiang(self):
            pass

子类继承了父类,必须写这个ChouXiang方法,不然它就没法工作,所以子类继承父类后必须重写某个类,那么这个类就叫做抽象类

原文地址:https://www.cnblogs.com/liaojiafa/p/6718152.html