资讯专栏INFORMATION COLUMN

Python理解面向对象

hatlonely / 1552人阅读

摘要:面向对象编程,简称,是一种程序设计思想。面向过程与面向对象面向过程的程序设计把函数作为程序的基本单元。以上是在计算机世界里认识面向对象和面向过程,接下来给大家举个生活中的例子就拿你早上想吃鸡蛋灌饼为例。

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程 与 面向对象

面向过程的程序设计把函数作为程序的基本单元。程序设计时,编写一组一组的函数,然后一步一步按照顺序的执行各个函数。通常为了简化程序,将大块函数通过切割成小块函数来降低系统的复杂度。

而面向对象的程序设计把对象作为程序的基本单元,程序设计时,设计许多的对象,每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递,各个对象调用相关的方法。

以上是在计算机世界里认识面向对象和面向过程,接下来给大家举个生活中的例子:

就拿你早上想吃 鸡蛋灌饼 为例。

如果是面向过程去实现的话:
1、准备食材(鸡蛋 面 油 盐 酱 生菜)
2、清洗食材(生菜)
3、和面
4、制作面饼
5、煎面饼
6、在面饼上打入鸡蛋
7、鸡蛋熟后,刷酱放入生菜

如果是面向对象去实现的话:
1、找到自己的女(男)朋友(对象),让她/他去给做或者买到鸡蛋灌饼。
2、如果没有女(男)朋友,就创造一个呗。然后让他帮你弄到鸡蛋灌饼...

世界万物皆对象,其实我们生成在这个世界上每天所看到接触的都是对象,比如你的鼠标、你的键盘,你的杯子,还有你其实都是对象。每个对象都有自己的属性。再给大家补充一个栗子,理解为什么需要面向对象,现在我们大家可以把自己看作上帝,给你一个地球你都想往里面放什么,自己想放什么放什么,首先山川、河流放好了,然后你为了让你的地球可以丰富一点,你开始加动物,比如鸟啊,那鸟就有很多种了,百灵鸟、麻雀、火烈鸟等等,那你作为上帝是不是该累死了,每天坐那捏鸟,多无聊,你是不是想轻巧点,那怎么办呢,你发现你捏的鸟把,其实也不多,我们来分析看看

鸟:颜色 形状 会飞 会叫
那不同的可能是鸟长的不太一样,或者叫声不一样,但是大部分还是一样,上帝也累呀,于是呢就想创造一个鸟出来,然后不同的鸟都是在这个基础上加工,这回是不是省事多了,那鸟造完了,还有鱼呢,还有人

Python的面向对象

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容

面向对象的几大核心特性:

Class 类:一个类指相同事物相同特征提取,把相同的属性方法提炼出来定义在类中

Object 对象:一个对象是类的实例,对象是具体的,类是抽象

封装:对外部世界隐藏对象的工作细节

继承:一个子类继承基类的字段和方法

多态:对不同类的对象使用同样的操作

创建类

首先创建类的语法

class ClassName(object):
    pass

python中使用class关键字修饰类,类名一般采用首字母大写,小阔号里面表示继承,所有类最后都继承自object。
在类中我们可以定义属性和方法,注意这个地方有一个叫法问题,方法其实和之前写的函数一样,但是在类中定义的称为方法,两个的区别在调用的时候,方法需要特定的对象,而函数不需要
下面我们来自己写一个简单的类看下

class Lufei:
    name="蒙奇·D·路飞"
    age=10
    def eatmeat(self):
        print("吃肉")


l=Lufei()      #实例化
print(l.name)  #调用类的属性
l.eatmeat()    #调用类的方法

在eatmeat的方法参数中有一个self,这个需要注意下一定不可以省略,对于在类中定义方法的要求,就是第一个参数必须时self,除第一个参数外,类的方法和普通函数没有什么区别,前面学会的可变参数、默认参数、关键字参数等等都可以直接使用。

self的重要性

不仅是__init__需要self作为第一个参数,类中定义的所有方法都需要。类代码设计为在所有对象实例间共享,self可以帮助标示要处理哪个对象实例的数据。
python要求每个方法的第一个参数为调用对象实例,其实我们来看下下面代码就懂了,来看下当我们实例话后python解释器做了什么

#我们写的
l=Lufei()
l.eatmeat()
#python执行的代码
Lufei.__init__(l)
l.eatmeat(l)
理解构造函数
class Lufei:
    def __init__(self,dream):
        print("类初始化的时候执行__init__")
        self.dream=dream

    name="蒙奇·D·路飞"
    age=10
    def eatmeat(self):
        print("吃肉"+self.dream)


l=Lufei("梦想是找到传说中的One Piece,成为海贼王")      #实例化
l.eatmeat()    #调用类的方法

通过上面我们来认识一个新的方法,__init__()方法是一个特殊的方法,在对象实例化的时候调用(init表示初始化的意思,是initialization的简写)注意前后两个下划线不可以省略,这个方法也叫构造方法
在定义类的时候,如果没有显示定义一个__init__()方法,程序默认调用一个无参的__init__()方法,但是要注意,一个类中只定义一个构造函数,编写多个实例化的时候会调用最后一个。

通过type类创建对象
#正常创建一个类
class Bird:
    def fly(self):
        print("i can fly")
 

print(Bird.__class__) #输出  
#那么我们知道Bird其实也是一个对象,通过type这个类进行实例化的

#通过type构造函数来参加一个类
# def fly(self):
#     print("i can fly")

# #type(“类名”,"基类",类的成员)
# Bird=type("Bird",(object,),{"fly":fly})    

b=Bird()
b.fly()
__new__和__init区别
class Bird:
    def __init__(self):
        print("init") #先执行new 再执行init

    def fly(self):
        print("fly")

    def __new__(self):
        print("new")
        return object.__new__(self) #如果没有return,这个对象并没有创建成功

b=Bird()
b.fly()

new 用来创建实例,在返回的实例上执行__init__,如果不返回实例那么__init__将不会执行
init 用来初始化实例,设置属性什么的

类变量和实例变量
class Lufei:
    def __init__(self):
        # pass
        self.name="路飞"  #实例变量,以self.开头

    name="蒙奇·D·路飞" #类变量
    age=10

l=Lufei()      #实例化
print(l.name) #访问实例变量,输出路飞
print(Lufei.name) #访问类变量,输出蒙奇·D·路飞

首先我们先来说下,实例的属性和类属性分别存储在对应的dict中,当我们输出查找的时候,它的顺序是 实例dict->类的dict->基类
那么我们区分类变量和实例变量的目的是什么呢,他们有什么区别:很重要的一点,实例变量为每个实例独有,类变量为所有实例共享,我们来通过修改和打印来看下

class Lufei:
    def __init__(self):
        pass
        # self.name="路飞"  #实例变量,以self.开头

    name="蒙奇·D·路飞" #类变量
    age=10

l=Lufei()      #实例化
# l.name="777"
Lufei.name="666" #修改的是类变量
print(l.name) #访问实例变量 
print(Lufei.name) #访问类变量 

l1=Lufei()   #实例化
print(l1.name) 

如果通过Lufei.name="666" 这句修改的话,则三个输出都是 666,因为类变量作用所有实例,如果通过l.name="777"这句话修改,则只有l这个实例输出777,l1实例输出蒙奇·D·路飞
类变量的用途:大家共用的属性放在类变量中

类的访问权限

在类中我们定义自己的属性和方法,通过实例化后的对象,可以在外部进行调用,但是我们也可以对属性和方法的访问权限进行设置,让外界无法访问。
在python中实例的变量名以__开头,就会变成私有变量(private)外部不能访问

class Lufei:
    def __init__(self,dream):
        print("类初始化的时候执行__init__")
        self.__dream=dream

    __name="蒙奇·D·路飞" #加__前缀变成私有变量
    age=10
    def eatmeat(self):
        print("吃肉"+self.__dream) #私有变量在外部无法访问,内部可以访问


l=Lufei("梦想是找到传说中的One Piece,成为海贼王")      #实例化
print(l.__name) #报错 "Lufei" object has no attribute "name"
l.eatmeat()    #调用类的方法

上面的代码中我们可以进行设置私有变量,那么私有变量都无法访问了,如果想要对其进行操作有什么方式呢,我们可以通过方法来解决,比如我们修改下私有变量为age

class Lufei:
    def __init__(self,dream):
        print("类初始化的时候执行__init__")
        self.__dream=dream

    name="蒙奇·D·路飞" 
    __age=10#加__前缀变成私有变量
    #通过方法或者私有变量返回
    def getage(self):
        return self.__age
    #通过方法对私有变量进行赋值操作,并可以进行数据安全验证
    def setage(self,age):
        if age<0:
            return "太小了"
        else:
            self.__age=age
            return "ok"
    def eatmeat(self):
        print("吃肉"+self.__dream) #私有变量在外部无法访问,内部可以访问


l=Lufei("梦想是找到传说中的One Piece,成为海贼王")      #实例化
l.eatmeat()    #调用类的方法

l.setage(100)#对私有变量年龄进行设置
print(l.getage()) #获取私有变量年龄

可以看到通过上面的代码,我们既可以获取到私有变量,又可以对你参数进行安全检查,既然变量可以,那方法是不是能设置为私有方法呢,答案肯定可以,和私有变量一样,在名字的前面加上__即可

#定义一个私有方法    
def __getdream(self):
   print(self.__dream)  #class外界无法访问,内部可以同self.__getdream()访问
静态方法、实例方法、类方法

class Bird:
    name="lalal"
    def fly(self):
        print("fly")
        print(self) #输出: <__main__.Bird object at 0x00000000022032E8>

    @classmethod
    def eat(cls):
        # 一个类方法就可以通过类或它的实例来调用的方法, 不管你是用类来调用这个方法还是类实例调用这个方法,该方法的第一个参数总是定义该方法的类对象。 
        # 记住:方法的第一个参数都是类对象而不是实例对象
        #类方法
        print("eat")
        print(cls) #输出:

    @staticmethod
    def sleep():
        #使用静态方法的好处是,不需要定义实例即可使用这个方法。另外,多个实例共享此静态方法。
        #使用了静态方法,则不能再使用self
        #静态方法不能访问类变量和实例变量
        # print(name)#报错
        print("sleep")  

b=Bird()
# Bird.fly(b) 传递self进去,不传递只是一个语法糖
b.fly()  #实例方法只能被实例对象调用
# 静态方法(由@staticmethod装饰的方法)、类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用
b.eat()
b.sleep()
#可以被类或类的实例对象调用
Bird.eat()
Bird.sleep()

#实例方法、类方法、静态方法区别:
#1.实例方法隐含的参数为类实例self,而类方法隐含的参数为类本身cls。
#静态方法无隐含参数,主要为了类实例也可以直接调用静态方法。
#2.静态方法是无法访问实例变量的
#3.类成员方法也同样无法访问实例变量,但可以访问类变量;
# 静态方法有点像函数工具库的作用,而类成员方法则更接近类似Java面向对象概念中的静态方法。

总结两点:

静态方法:无法访问类属性、实例属性,相当于一个相对独立的方法,跟类其实没什么关系,换个角度来讲,其实就是放在一个类的作用域里的函数而已。

类成员方法:可以访问类属性,无法访问实例属性。上述的变量val1,在类里是类变量,在实例中又是实例变量,所以容易混淆。

继承

我们先来说下继承的概念,实现重用的方法之一就是通过继承机制,就像生活中,子女继承父母的财产,当然也有可能是蚂蚁花呗
继承的语法,在定义好的类小括号里面写上要继承的类名,这个时候,被继承的类我们称为父类或者基类,继承的类的称为子类或者派生类,我们来看下具体的代码实现

#父类
class Long:
    name="蒙奇·D·龙"
    def Getdream(self):
        print("推翻世界政府,改变世界抹去不需要人的规则。建立和谐,自由,平等,充满梦想的世界。")
#子类
class Lufei(Long):
    pass

l=Lufei() #实例化子类
l.Getdream() #从父类继承来的

Lufei是子类,Long是父类,子类可以继承父类非私有的所有属性和方法
上面代码中属于单继承,python还支持多重继承,在小括号里面可以通过都好分隔写多个父类的名称,需要注意的是当多个父类的时候,python会从左到右搜索

#父类
class Long:
    name="蒙奇·D·龙"
    def Getdream(self):
        print("推翻世界政府,改变世界抹去不需要人的规则。建立和谐,自由,平等,充满梦想的世界。")
class Hongfa:
    name="香克斯"

#子类
class Lufei(Long,Hongfa):
    pass

l=Lufei() #实例化子类
print(l.name) #当访问的属性两个父类中都有定义的时候以第一个为主
l.Getdream() #从父类继承来的

好了,我们这就完成了多重继承,一个子类继承了多个父类,同时获得了多个父类的所有非私有功能

super

在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print("父类中的")

class Dog(Animal):
    def greet(self):
        super().greet()  #通过super访问父类中的方法
        # 当然 Python 2 里super() 是一定要参数的 super(Dog, self).greet()
        print("子类中的")

d=Dog("旺财")
d.greet()       

在子类中除了super还可以通过父类名称.方法去进行调用,但是我们选择super的另一个好处是他避免硬编码和在多重继承上面所发挥的

什么是硬编码?硬编码一般指在代码中写死的,与它相对应的是配置项,可以在程序发布后进行修改

MRO 列表
对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置。而搜索的顺序就是所谓的「方法解析顺序」(Method Resolution Order,或MRO)。对于只支持单继承的语言来说,MRO 一般比较简单;而对于 Python 这种支持多继承的语言来说,MRO 就复杂很多。

super在上面的代码访问父类的方法没任何问题,那么如果是访问父类的父类呢,我们先初始化一个场景

class A(object):
    def __init__(self):
        print("A")
    def show(self):
        print("A中的show")           
class B(A):
    def __init__(self):
        super().__init__()
        print("B")
 
class C(A):
    def __init__(self):
        super().__init__()
        print("C")
    def show(self):
        #重写了父类中的show
        print("C中的show")  

class D(B,C):
    def __init__(self):
        super().__init__()
        print("D")  

通过上面的代码,我们可以得到下图

那么问题来了,这个时候 d1.show()的结果会是什么?

说会MRO,事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:

print(D.__mro__) #输出(, , , , )

Python 至少有三种不同的 MRO:
1.经典类(classic class)的深度遍历。
经典类采用了一种很简单的 MRO 方法:从左至右的深度优先遍历。以上述「菱形继承」为例,其查找顺序为 [D, B, A, C, A],如果只保留重复类的第一个则结果为 [D,B,A,C]
2.Python 2.2 的新式类(new-style class)预计算。
3.Python 2.3 的新式类的C3 算法。它也是 Python 3 唯一支持的方式。

多态

多态来自于希腊语,意思是有多种形式,多态意味着即使不知道变量所引用的对象类型是什么,也能对对象进行操作,多态会根据对象的不同而表现出不同的行为

#父类
class Long:
    name="蒙奇·D·龙"
    def Getdream(self):
        print("推翻世界政府,改变世界抹去不需要人的规则。建立和谐,自由,平等,充满梦想的世界。")

#子类
class Lufei(Long):
    name="蒙奇·D·路飞"
    def Uniqueskills(self):
        print("三档")
#子类
class Aisi(Long):
    name="艾斯"
    def Uniqueskills(self):
        print("火拳")   

#定义父类作为参数,所有的子类都可以传参进去
def Show(Long):
    Long.Uniqueskills()

Show(Lufei()) #输出三挡
Show(Aisi())  #输出火拳

有没有理解多态,其实很简单,多态我们不用对具体的子类型进行了解,到底调用哪一个方法,在运行的时候会由该对象的确切类型决定,使用多态,我们只管调用,不用管细节

封装

其实我们从学习函数以来都在提及封装的概念,封装我们可以理解为,不用管具体的实现细节,直接调用即可,就像我们看电视,完全不用管电视是怎么播放的,只需要按下按钮可以观看即可

析构函数

当使用del 删除对象时,会调用他本身的析构函数,另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。
__del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数

class Lufei:
    def __init__(self):
        self.name="路飞" 

    def __del__(self):
        print("挂了")

析构函数:在实例释放、销毁的时候执行、通常用于做一些收尾工作,如关闭一些数据

类中特殊成员
class BigBrid:
    """描述类的信息""" #BigBrid.__doc__ 可以看到    
    name="bb"
    def eat(self):
        print("吃")
    def __str__(self):
        return "lidao"
    
b=BigBrid()
b.name="lala"
print(b.__doc__) #输出:描述类的信息
print(b.__module__)#输出:__main__
print(b.__class__)#输出:
#__init__ 构造方法,通过类创建对象时,自动触发执行
#__del__析构方法,当对象在内存中被释放时,自动触发执行。
print(BigBrid.__dict__) #获取类的成员 输出:{"__module__": "__main__", "__doc__": "描述类的信息", "name": "bb", "eat": , "__dict__": , "__weakref__": }
print(b.__dict__) #获取 对象b 的成员 输出:{"name": "lala"}
#__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
print(b) #输出 lidao

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41195.html

相关文章

  • [零基础学python]关于类的初步认识

    摘要:反对者在某些领域对此予以否认。下面再引用一段来自维基百科中关于的历史。类的更严格的定义是由某种特定的元数据所组成的内聚的包。类还可以有运行时表示形式元对象,它为操作与类相关的元数据提供了运行时支持。 在开始部分,请看官非常非常耐心地阅读下面几个枯燥的术语解释,本来这不符合本教程的风格,但是,请看官谅解,因为列位将来一定要阅读枯燥的东西的。这些枯燥的属于解释,均来自维基百科。 1、问题...

    王岩威 评论0 收藏0
  • python面试题

    摘要:今天我们介绍的主角是在类的继承里面非常常用,它解决了子类调用父类方法的一些问题,父类多次被调用时只执行一次,优化了执行逻辑,下面我们就来详细看一下。 1 谈谈你对面向对象的理解? 面向对象的编程---object oriented programming,简称:OOP,是一种编程的思想。OOP把对象当成一个程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的出现极大的提高了编...

    jeffrey_up 评论0 收藏0
  • Python中 什么是面向过程

    摘要:文字有点长,对于不想看文字的朋友,可以去这里看视频,内容和这个文字一样的,视频可能更好理解面向过程概述所谓面向过程,及关注过程面向的字面意思就是关注着眼于面对着那么什么是过程呢过程就是过程,也可以理解为步骤当我们做某件事情的时候,如果关注过 文字有点长,对于不想看文字的朋友,可以去这里看视频,内容和这个文字一样的,视频可能更好理解 https://www.piqizhu.com/v/...

    hot_pot_Leo 评论0 收藏0
  • 如何系统地自学 Python

    摘要:这里推荐一本书源码剖析源码剖析豆瓣这本书把源码中最核心的部分,给出了详细的阐释,不过阅读此书需要对语言内存模型和指针有着很好的理解。   是否非常想学好 Python,一方面被琐事纠缠,一直没能动手,另一方面,担心学习成本太高,心里默默敲着退堂鼓?   幸运的是,Python 是一门初学者友好的编程语言,想要完全掌握它,你不必花上太多的时间和精力。   Python 的设计哲学之一就是...

    zgbgx 评论0 收藏0
  • Python OOP 面向对象编程

    摘要:时代,如果需要手动继承,如多态多态是指,不同的子类对象调用相同的父类方法,会产生多态多样结果的编程特性。 参考:黑马程序员教程 - Python基础 面向对象 OOP三大特性,且三个特性是有顺序的: 封装 继承 多态 封装 指的就是把现实世界的事务,封装、抽象成编程里的对象,包括各种属性和方法。这个一般都很简单,不需要多讲。 唯一要注意的就是:推荐从小往大开始封装、开发类。比如手枪...

    weknow619 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<