python面向对象

5.2 面向对象

面向过程与面向对象的区别

面向过程,面向对象,它俩都是编程思想,就是解决问题的思路有差别

举例:我要去吃饭 饭怎么来

面向过程:去买菜,回家洗菜,做饭,亲力亲为,事情是由自己来做的

  • 事情自己做,费时间,费精力,执行者(自己做)

面向对象:下馆子,叫外卖,饭不是自己做的,而是由另外一种事物来做

  • 事情别人做,省时间,省精力,指挥者(指挥别人做)

我们之前写到Python代码,大部分都是面向过程的!都是自己一步一步来实现问题的求解步骤的,只不过在某一些细节,我们用到了面向对象的知识,比如字符串对象内置的函数(s.upper()),比如列表对象内置的函数(l.append()),相当于把这些对象给他拟人化,我们相当于是指挥这些事物来给我们做事情,至于它们具体是怎么做的,我们没有必要操心,我们只关注操作后的结果即可!

我想吃饭-不想做-叫外卖(面向对象)-餐馆-接到了我的餐饮订单-餐馆就得开始做饭(面向过程)

面向过程的思想和面向对象的思想,是互斥的关系吗?不是的

所有的代码宏观上而言都是面向过程(一步一步来求解问题的),只不过在某些步骤中,会使用别的对象自带的功能来帮我们处理一些问题

# 输入10个数据并进行排序
# 输入10个数据并进行排序
arr = []
for i in range(0,10):
    arr.append(input())
arr.sort()
for i in range(1,len(arr)):
....插入排序
arr = []
for i in range(0,10):
    arr.append(input())
arr.sort()
for i in range(1,len(arr)):
....插入排序

代码是否完全由可以用面向对象的思路去做呢?

A-B-C-D-E.......依次往复下去,这个事始终没有被解决,只要这个事要解决,那么肯定是要一步一步实现的,所以这个时候用到的就是面向过程的思路!对象.方法()

代码可以完全面向过程,但是不能完全面向对象!完全自己实现功能

类与对象的区别

什么是对象?所谓的对象就是现实中真正存在的事物,在计算机领域中,对象就是在硬盘或内存中真实创建出来的数据。

什么是类?所谓的类就是多个具有相同属性和行为的对象的统称

  • 我喜欢狗:狗类

  • 我喜欢你家的狗:对象

那么在实际生活中,先有对象,还是先有类?先存在各种事物(对象),类主要是由人对世界上的事物进行划分的概念

那么在计算机中,先有对象,还是先有类?先有类,我们程序员是通过计算机对世界的仿真和模拟来实现编程,所以事先将相关类别的信息告诉计算机,然后计算机再通过这些类的描述再去创建相关的数据对象。

如何来描述一个类(应该具有哪些信息)?

  • 她的高子很高:身高 - 属性 - 数据 - 数字

  • 她的名字很好听:姓名 - 属性 - 数据 - 字符串

  • 他的智商很高:智商 - 属性 - 数据 - 数字

  • 他打篮球打的好:打篮球 - 行为(功能)- 函数

  • 她唱歌很好听:唱歌 - 行为(功能) - 函数

综上说所属,描述一个类的时候,我们重点描述的是这个事物属性行为

在编程当中,只有类先定义出来,才能去创建相关的对象

  • 类是一个大楼的图纸,具体的一个大楼 就是该图纸的一个对象

如何创建类和对象

在计算机当中,先有类,后有对象,尤其是在Python语言当中:

class 类名:
    def __init__(self,...):     # 构造函数
        self.属性1 = 属性值      # 实例变量 
        self.属性2 = 属性值
    def method(self,...):       # 实例函数
  • 类名:就是你要描述事物的名称

  • 构造函数__init__(self):就是在创建对象的过程中,对对象相关的属性进行初始化的,只能在创建对象的时候使用,但凡对象一旦创建完毕 ,则该函数不能在被对象调用

  • 实例变量:就是指每一个对象的属性 对象的特有属性 每个对象都有 但是值会不一样

  • 实例函数:就是指每一个对象的行为 从内存角度而言只有一份 在方法区中 类的二进制代码里 如果要被对象调用 用self关键字来进行区分即可 只有对象创建之后才能通过对象来调用

  • self:关键字 当前对象的引用

  • class:关键字 类定义

举例:创建圆这个类

  • 属性:半径

  • 行为:求面积,求边长

# 类的定义
class Circle:       # 定义Circle这个类
    def __init__(self, radius = 1): # 构造函数 radius = 1 默认参数 
        self.radius = radius        # 创建实例变量self.radius 它的值等于传入的radius的值
    def getArea(self):              # 实例函数 用于计算面积
        return self.radius ** 2 * 3.14  # 根据自身的半径,来计算面积
    def getPermeter(self):          # 实例函数 用于计算周长
        return 2 * 3.14 * self.radius   # 根据自身的半径,来计算周长
# 创建Circle对象 其实就是在调用__init__()函数
c1 = Circle()   # 创建Circle对象 但是不指定radius的值
c2 = Circle(10) # 创建Circle对象 指定radius的值为10
​
# 对象.函数() 对象.属性 来调用对象的内容
print(c1.radius)
print(c2.radius)
​
print(c1.getArea())
print(c1.getPermeter())
​
print(c2.getArea())
print(c2.getPermeter())

  • 相关内存

    • 栈内存:用于执行函数的

    • 堆内存:存放数据对象

    • 方法区:存储的是编译好的二进制代码,主要存放的就是类的二进制代码

  • 将Circle这个类的二进制代码加载进方法区中

  • 执行c1 = Circle()这段代码

  • 现在堆内存中开辟一个空间,该空间有其真实的物理内存地址0x123

  • 调用对应的构造函数进栈执行__init__(self,radius = 1),当前对象是Circle类的实例,所以从方法区中Circle二进制类代码中调用

  • 构造函数进栈之后,先区分是哪个对象调用的该构造函数,是0x123这个对象调用的,所以该构造函数中形式参数self(局部变量)存储的就是0x123这个值,然后外界并没有传入radius的值,所以构造函数中形式参数(局部变量)radius默认值为1

  • self.radius = radius将局部变量radius的值给对象当中的radius,那么是给哪个对象呢?self = 0x123,所以给的是0x123对象中的radius实例变量(由于对象该空间中开始没有radius变量,所以第一次赋值即创建)

  • c1 = Circle()构造函数执行完毕之后,将对象的地址给c1,c1存储的是第一个Circle对象的地址0x123

  • 同理 c2 = Circle(10),步骤和上述一样,只不过给构造函数传递了radius的实际参数10

  • c1.getArea(),先读取c1变量中的地址0x123,然后在堆内存中找0x123对应的对象,找到之后,发现这个对象是Cilcle类的一个对象,所以在调用getArea()方法时,从方法区中Circle二进制代码中调用的

  • getArea()进栈执行,为了区分是哪个对象调用的getArea(),此时self表示为c1所存储的地址,self = 0x123,所以在getArea()中,关于self的操作都是找0x123这个对象的,计算完毕之后返回值并弹栈

在类和对象中不同变量的作用域

  • 实例变量:在对象的内存中,每个对象都有各自的实例变量,属于对象特有数据,在类中(类中函数里),调用实例变量的话,必须是self.xxx形式,在外通过对象调用,在类内部全局可访问

  • 类变量:在方法区中存在,是每个对象的共享数据,每个对象都可以访问到该数据,通过对象.xxx形式,也可以通过类.xxx形式访问,如果实例变量和类变量重名的话,对于对象.xxx形式它调用的是实例变量,类.xxx形式只能调用类变量,不能调用实例变量

  • 形式参数变量:主要在函数的参数列表中体现,可以有默认值,也可以没有,具体的值由外界的实际参数来传入,它所存储的位置在其所属函数的内部,作用域在当前函数内部

  • 函数内部局部变量:它并没有体现在函数的参数列表中,它是直接在函数内部中创建的变量,作用域在当前函数内部

  • 全局变量:它是类之外的变量,主要在类的内部尤其是实例函数或者构造函数中调用,前提就是在当前函数的内部,并没有其他的局部变量和其重名的话,如果重名,调用的就是函数内部的局部变量,但是我们一般不去使用,有危险性的,它压根就不属于类内部的数据!

class Demo:
    num = 1000      # num 类变量 全部对象共享的数据 可以使用对象调用 也可以使用类调用
    def __init__(self,num):
        print(num)  # num 是init函数的局部变量(形式参数)
        self.num = 666
    def showA(self):
        print(num)  # num 是全局变量(类之外的变量)
    def showB(self):
        print(self.num) # num 对象中的实例变量
    def showC(self):
        num = 888   
        print(num)  # num 函数中创建的局部变量
    def showD(self):
        print(num)  # num 是全局变量(类之外的变量)
# main
num = 100
d1 = Demo(10)
d1.showA()
d1.showB()
d1.showC()
print(num)
d1.showD()
print(Demo.num)
print(d1.num)

数据安全性的问题 - 私有化问题

对于上述的内容,我们发现,外界可以通过对象.xxx的形式来访问对象内部的实例变量,同时也可以在外部修改该对象的实例变量的值,但是从业务逻辑而言,这种修改是不可靠不安全的

对象的实例变量是属于对象的一种特有数据,外界如果要访问对象的特有数据,需要“征得”对象同意的,并且也不能随意更改对象的特有数据。

举例:小张,看一下你内裤的颜色

所以说明,有些对象的特有数据是不能随意向外界公开的,也就意味着外界不能调用对象的特有数据,此时我们就得需给这些实例变量加上私有权限,self.__xxx对象的特有数据进行私有化(类的内部可以随意访问,但是在类的外部不能访问)

举例:小张,你把你的心脏给我看看

心脏这个数据不能随意给外界看的,所以心脏这个数据我们需要私有化,一旦私有化之后,外界就不能访问了。但是,如果小张出现了心脏病继续做手术,医生需要开膛,对心脏做手术,这个时候小张能拒绝吗?不能,医生还是要访问的。

综上所述,我想给大家表达的一个概念,对于对象的特有属性,它不是不能提供给外界,但就怕一旦提供给了外界,外界就可以随意修改,对象一点脾气也没有,那我们是不是应该将修改的主体意识转为对象,需要对象的同意你才能进行修改。外界建议对象修改数据,至于对象该不该,看对象自己!从代码的角度而言,如果实例变量一旦被私有化,它既不能被外界修改,同时也不能被外界访问。

相对而言修改的危害要大于访问的危害,你为了拒绝外界直接修改,一竿子打死,私有化,外界虽然修改不了,但也访问不了

处理的方式:

  • 对没有必要向外界公开的数据进行私有化

  • 向外界提供可以修改数据的方法,在方法的内部,对外界传入的修改建议进行甄别,由对象自身来决定是否修改setXXX(data)修改器

  • 向外界提供可以访问数据的方法getXXX()访问器

class Student:
    # name age id 位置参数 按顺序传递 位置参数必须要满足的 必须要传递的 数量必须是一致的
    # sc1 sc2 sc3 默认参数 可传可不传(默认值) 但是必须在位置参数之后
    def __init__(self, name, age, id, sc1 = 0 , sc2 = 0, sc3 = 0):
        self.name = name
        self.__age = age
        self.id = id
        self.sc1 = sc1
        self.sc2 = sc2
        self.sc3 = sc3
        self.sc = self.sc1 + self.sc2 + self.sc3
    def speak(self):
        print("大家好,我%s,今年%d岁,学号是%s,语文%d分,数学%d分,英语%d分,总分%d" % (self.name,self.__age,self.id,self.sc1,self.sc2,self.sc3,self.sc))
    def getSC(self):
        return self.sc
    def getAge(self):
        return self.__age
    def setAge(self, age):
        if age > 0:
            self.__age = age
​
# age已经私有化了 访问不了 修改不了
# print(s1.age)
# s1.age = -10
print(s1.getAge())
s1.setAge(-20)
s1.setAge(100)
s1.speak()

额外的补充,如果对象有一些实例函数也不想被外界访问的话,也可以将这些函数进行私有化

"""
定义点类
"""
class Point:
    def __init__(self,x = 0,y = 0):
        self.__x = x
        self.__y = y
    
    def distance(self, otherPoint):
        x1 = self.__x
        y1 = self.__y
        x2 = otherPoint.getX()
        y2 = otherPoint.getY()
        return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5
​
    def setX(self,x):
        self.__x = x
​
    def setY(self,y):
        self.__y = y
​
    def getX(self):
        return self.__x
    
    def getY(self):
        return self.__y
    def __show(self):
        print(self.__x, self.__y)
​
p1 = Point()
p2 = Point(1,1)
print(p1.distance(p2))
p1.show() # 函数被私有化了 外界访问不了了

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值