python之面向对象编程

python之面向对象编程

一、对象的概念

1.从“过程”过度到“对象”的概念介绍

引入:了解面向过程
面向过程:
  • 核心是过程二字,即先做什么,在做什么,最后做什么。

  • 目的:过程的终极奥义就是将程序流水化

  • 作用: 过程是"流水线",用来分步骤解决问题的

  • 优点:将复杂的问题流水化,进而简单化

  • 缺点:一整个流程只为了解决一个问题,换另一个问题又需要另一个整套的流程, 牵一发而动全身

面向对象:
  • 核心是对象二字,对象是特征与技能的集合体
  • 目的:对象的终极奥义就是将程序“整合”
  • 作用:对象是容器,用来盛放数据(特征)与功能(技能)
面向对象的好处:

提高程序的解耦合性, 进而提升程序的可扩展性( 一个软件质量因素有很多: 性能, 可维护, 可扩展, 可移植, 成本, 安全…, 而面向对象解决的仅仅是可扩展性的问题, 所以它也是有局限性的)

img

2.了解“对象”之前我们的数据和功能都是分开的

  • 在了解到“对象”之前,程序的数据和功能都是分离开的

    #数据: stu_name, stu_age, stu_sex
    stu_name="淘小欣"
    stu_age="2"
    stu_sex="male"
    
    #功能(函数):tell_info, set_info
    # 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能tell_info,另外一类则是多个数据name、age、sex,然后才能执行,非常麻烦
    
    def tell_info():
        print('名字:%s 性别:%s 年龄:%s' % (stu_name, stu_age, stu_sex))
        
    def set_info():
        stu_name = 'JACK'
        stu_age = 22
        stu_sex = "female"
    

3.了解了“对象”以后我们把数据和功能进行整合

了解到”对象“之后,将程序的数据和功能进行整合到一起,提高程序的可拓展性

  • 学生的容器=学生的数据+学生的功能
def tell_info():
    print('名字:%s 性别:%s 年龄:%s' % (stu1_obj['stu_name'], stu1_obj["stu_age"], stu1_obj['stu_sex']))


def set_info(name, age, sex):
    stu1_obj['stu_name'] = name
    stu1_obj['stu_age'] = age
    stu1_obj['stu_sex'] = sex


stu1_obj = {
    'stu_name': 'JACK',
    'stu_age': 22,
    'stu_sex': 'female',
    'tell_info': tell_info,
    'set_info': set_info,
}

二、类与对象

前言

  • 在没有学习类这个概念时,数据与功能是分离的
  • 每次调用都需要重复传入一堆参数, 我们能想到的解决方法是,把这些变量都定义成全局变量
  • 这样我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用
  • 然而事实上只有某一些对象才使用某一些它们所需要的功能, 并不是全部的功能都需要
  • 言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了

什么是类?

  • 类, 即类别, 种类, 是面向对象设计最重要的概念, 对象是特征与技能的集合体, 而类则是一系列对象相似特征与技能的集合体
  • 类也是"容器",该容器用来存放同类对象共有的数据与功能

image-20201222104602275

类与对象的关联

产生对象的类与对象之间关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。

强调:在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)

现实世界与程序中类与对象的关系

现实世界:

  • 现实世界中 : 先有对象, 再有类
  • 世界上现实有了各种各样的物种:猪,狗,猫,人,然后我们将其归为:猪类,犬类,猫类,人类

在程序中:

  • 在程序中 : 先有类, 再有对象
  • 与函数类似, 先定义再调用, 类也是一样, 先定义类, 再调用类
  • 不同的是函数调用时执行函数体代码, 而类则是实例出一个对

对象归类的好处

极大地节省了空间和减少代码冗余: 把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可

三、面向对象编程

类的定义与示例化

类的定义
  • 类名尽量用驼峰体

  • 类是同类对象们相似功能的集合体

  • 类体代码的范围:类体代码最常见的是变量与函数名的定义, 但是类体代码其实是可以包含任意其它代码的.

  • 类体代码的生效时间(注意): 类体代码在定义阶段就会立即执行, 会产生类的名称空间. 我们可以是用 类名.__dict__ 访问到产生的类体代码的名称空间.

    # 一. 类的定义
    class Student:
        # 变量的定义
        stu_school = '淘小欣的大学'
    
        # 函数功能的定义
        def tell_stu_info(stu_obj):
            print('学生信息:名字:%s 年龄:%s 性别:%s' % (
                stu_obj['stu_name'],
                stu_obj['stu_age'],
                stu_obj['stu_gender']
            ))
    
        def set_info(stu_obj, x, y, z):
            stu_obj['stu_name'] = x
            stu_obj['stu_age'] = y
            stu_obj['stu_gender'] = z
    
        print('========>')  # 类体代码在定义阶段就会立即执行
    
    
__dict__:类的名称空间
  • 类中可以有任意的 Python 代码, 这些代码在类定义阶段就会执行
  • 因而会产生新的名称空间, 用来存放类的变量名与函数名, 可以通过[类名].__dict__来进行查看
  • 同理, 对象的名称空间也是使用.__dict__来查看
#"."(点)专门用来访问属性, 本质上就是在操作"__dict__"

# 1. 类名.__dict__ 访问到产生的类体代码的名称空间.
print(Student.__dict__) 

'''输出结果
{'__module__': '__main__', 'stu_school': '淘小欣的大学', 'tell_stu_info': <function Student.tell_stu_info at 0x000001DCEB68DE50>, 'set_info': <function Student.set_info at 0x000001DCEB68DDC0>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
'''

# 2. 属性访问(属性访问的语法)
# 访问数据属性
print(Student.stu_school)  # 等同于Student.__dict__['stu_school']
# 访问函数属性
print(Student.set_info)  # 等同于Student.__dict__['set_info']

# 3. 属性修改与添加(Student类体代码中 没有如下x数据属性就添加 有就修改)
Student.x = 1111  # Student.__dict__['x']=111
print(Student.__dict__)

del Student.x        # 等于字典类的操作 : Duck.__dict__.pop("Student.x")

定制对象独有的特征

类的示例化

  • 调用类产生对象,而调用类的过程称之为实例化

    #调用类产生对象
    stu1_obj = Student()
    stu2_obj = Student()
    stu3_obj = Student()
    
    # 因为这三个对象没有定制自己独有的属性, 因此都是空对象, 它们的名称空间都是{}
    print(stu1_obj.__dict__)
    print(stu2_obj.__dict__)
    print(stu3_obj.__dict__)
    
    #为对象定制自己独有的属性
    stu1_obj.stu_name = '淘小欣'  # stu1_obj.__dict__['stu_name']='淘小欣'
    stu1_obj.stu_age = 18  # stu1_obj.__dict__['stu_age']=18
    stu1_obj.stu_gender = 'male'  # stu1_obj.__dict__['stu_gender']='male'
    print(stu1_obj.__dict__)
    
    stu2_obj.stu_name = 'lili'
    stu2_obj.stu_age = 19
    stu2_obj.stu_gender = 'female'
    print(stu2_obj.__dict__)
    
    stu3_obj.stu_name = 'jack'
    stu3_obj.stu_age = 20
    stu3_obj.stu_gender = 'male'
    print(stu2_obj.__dict__)
    
    # 问题1: 代码冗余
    # 问题2: 属性的查找顺序
    
    • 解决问题一: 既然是代码冗余, 我们可以定义函数, 解决这个问题

      def init(stu_obj, name, age, sex):
          stu_obj.stu_name = name
          stu_obj.stu_age = age
          stu_obj.stu_sex = sex
      
      
      init(stu1_obj, '淘小欣', 18, 'male')
      init(stu2_obj, 'lili', 19, 'female')
      init(stu3_obj, 'jack', 20, 'male')
      
      print(stu1_obj.__dict__)  # 通过以上我们定义的init函数我们成功为3者对象创建了属于它们独有的属性
      print(stu2_obj.__dict__)
      print(stu3_obj.__dict__)
      
__init__方法
调用类产生对象

调用类的过程中又称之为实例化, 这里发生的3件事:

  1. 先产生一个空对象
  2. python会自动调用__init__方法, 然后将空对象和调用类时括号内的参数以一同当作实参传给__init__方法
  3. 最后返回初始化完成的对象
__init__方法
  • 该方法是为对象初始化自己独有的特征
#解决方案二:在调用类产生对象
class Student:
    # 变量的定义
    stu_school = '淘小欣的大学'
    # 注意:绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但命名为self是约定俗成的。
    # 空对象,'淘小欣',18,'male'
    def __init__(self, x, y, z):
        self.stu_name = x
        self.stu_age = y
        self.stu_gender = z

    # 函数功能的定义
    def tell_stu_info(stu_obj):
        print('学生信息:名字:%s 年龄:%s 性别:%s' % (
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj, x, y, z):
        stu_obj['stu_name'] = x
        stu_obj['stu_age'] = y
        stu_obj['stu_gender'] = z

#调用类产生对象
stu1_abj=Student('淘小欣', 18, 'male')  #Student.__init__(空对象,x,y,z)
stu2_abj=Student('lili', 19, 'female')
stu3_abj=Student('jack', 20, 'male')
print(stu1_abj.__dict__)
print(stu2_abj.__dict__)
print(stu3_abj.__dict__)
  • 解决问题二:

    class Student:
        # 在类Student中使用__init__方法会在调用阶段就会执行__init__的功能并进行如下方式把参数传入.
        # 空对象, '淘小欣', 18, 'male'
        def __init__(stu_obj, name, age, sex):
            stu_obj.stu_name = name  # 空对象.__dict__['stu_name'] = '淘小欣'
            stu_obj.stu_age = age  # 空对象.__dict__['stu_age'] = 18
            stu_obj.stu_sex = sex  # 空对象.__dict__['stu_sex'] = 'male
    
        def tell_stu_info(stu_obj):
            print('学生信息:名字:%s 年龄:%s 性别:%s' % (
                stu_obj['stu_name'],
                stu_obj['stu_age'],
                stu_obj['stu_gender']
            ))
    
        def set_info(stu_obj, x, y, z):
            stu_obj['stu_name'] = x
            stu_obj['stu_age'] = y
            stu_obj['stu_gender'] = z
    
总结__init__方法
  1. 会在调用类时自动触发执行, 用来为对象初始化自己独有的数据

  2. __init__内应该存放的是为对象初始化属性的功能, 但是是可以存放任意其他代码, 想要在类调用是就立刻执行的代码都可以放到该方法内

  3. __init__方法的返回值必须返回值None(提示: 无需指定, 默认返回None),否则报错。

四、对象属性查找顺序与绑定

对象的属性查找

1.类的两种属性
  • 类的数据属性是所有对象共享的

  • 类的函数属性是绑定给对象用的

    class Duck:
        master = "淘小欣"
        def __init__(self,price,size):
            self.price = price
            self.size = size
        def duck_info(self):
            print(f"鸭子信息,价格:{self.price},大小:{self.size}")
    duck1 = Duck(10,100)
    duck2 = Duck(15,80)
    
    # 类中存放的是对象共有的数据和功能
    # 类可以访问:
    # 1、类的数据属性
    print(Duck.master)
    # 2、类的函数属性
    print(Duck.duck_info)
    
2.数据属性共享
  • 数据 ID 都一样

ps : id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准

class Duck:
    master = "淘小欣"
    def __init__(self,breed,size):
        self.breed =breed
        self.size = size

duck1 = Duck("嘿鸭",100)
duck2 = Duck("好的鸭",80)

# 但其实类中的东西是给对象用的
# 类中定义的数据属性是共享给所有对象用的,大家访问的地址都一样.
print(id(duck1.master))  # 1905145581488
print(id(duck2.master))  # 1905145581488
3.函数属性绑定给对象
  • obj(对象).method(方法) 称为绑定方法, 内存地址都不一样
class Duck:
    master = "jack"
    def __init__(self,breed,size):
        self.breed =breed
        self.size = size
    def run(self):
        print(f"{self.breed}在跑")

duck1 = Duck("嘿鸭",100)
duck2 = Duck("好的鸭",80)
duck3 = Duck("别鸭",80)

# 绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入
# 类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
print(Duck.run)   #<function Duck.run at 0x000002044C08DDC0>
print(duck1.run)   # <bound method Duck.run of <__main__.Duck object at 0x0000020449852520>>
print(duck2.run)    #<bound method Duck.run of <__main__.Duck object at 0x000002044C084A60>>
print(duck3.run)    #<bound method Duck.run of <__main__.Duck object at 0x000002044C084BB0>>
4.对象的查找
  • 对象寻找属性, 比如 name, 它会先到自己的名称空间里面去找, 找不到再去类里面去找, 类中找不到再去父类, 父类中找不到就抛出异常

82ca289a1f35444a979154657e883b5

  • 类中定义的属性, 对象只能使用, 不能修改, 对象如果修改, 改的是自己的名称空间
# 对象更改属性, 改的只是自己的
class Duck:
    master = "jack"
    def __init__(self,breed,size):
        self.breed =breed
        self.size = size
    def run(self):
        print(f"{self.breed}在跑")

duck1 = Duck("不要鸭",80)
duck2=Duck("周黑鸭",25)

duck2.master = "淘小欣"  # 更改属性
print(Duck.master)     # jack
print(duck1.master)    #jack
print(duck2.master)     # 淘小欣

Ps:对象想改变类中的属性是不被允许的, 对象对默认不存在的属性会在自己局部中申明, 并不会影响其他对象, 更不会修改类的数据属性.


# 修改类的属性:类的数据属性只能通过类改变, 一旦改变类的数据属性, 所有对象共用的类的数据属性也将会被改变
class Duck:
    master = "jack"
    def __init__(self,breed,size):
        self.breed =breed
        self.size = size
    def run(self):
        print(f"{self.breed}在跑")

duck1 = Duck("酱紫鸭",80)
Duck.master = "淘小欣" # 类修改数据属性

print(Duck.master)   # 淘小欣
print(duck1.master)  # 淘小欣

对象的绑定方法

1.什么是对象的绑定方法
  • 定义在类内部的函数 (且没有被任何装饰器装饰的) 就是对象的绑定方法, 对象来调用, 调用时会自动传入对象本身 self
  • 对象的绑定方法, 类也可以调用, 当类调用的时候, 调的就是一个普通的函数, 有几个参数就要传几个参数
  • 可以通过 self 来区分到底是哪个对象调用了自己的绑定方法

ps : self 可以是任意名字,但是约定俗成地写成 self

2.什么叫函数, 什么叫方法?
  • 函数就是 def 定义的函数, 有几个参数就要传几个参数
  • 方法是绑定给对象使用的, 由对象来调用, 特殊之处就是会把对象自身传入, 与方法 __init__是一样的道理
3.对象调用绑定方法演示
  • 对象调用, 将对象自身传入
class Duck:
    master = "jack"
    def __init__(self,breed,size):
        self.breed =breed
        self.size = size
    def run(self):
        print(f"{self.breed}在跑")

    def eat(self):
        print(f"{self.breed}在吃东西")

    def sleep(self):
        print(f"{self.breed}在睡觉")

duck1 = Duck("可恶鸭",80)
duck2 = Duck("厉害鸭",50)
duck3 = Duck("好惨鸭",90)

duck1.run()    # 可恶鸭在跑
duck2.eat()    # 厉害鸭在吃东西
duck3.sleep()  # 好惨鸭在睡觉
4.类调用方法演示
  • 其实类中的函数主要是给对象来使用的, 这里只做演示
  • 类调用时要遵循函数的参数规则, 有几个参数就要传几个参数
# 类调用自己的函数属性必须严格按照函数的用法来
Duck.run(duck1)   # 可恶鸭在跑
Duck.eat(duck2)   # 厉害鸭在吃东西
Duck.sleep(duck3) # 好惨鸭在睡觉

五.Python中一切皆对象

1.“类” 与 “类型”

  • 在Python3中,“类”“类型”同一个概念, 我们其实早就使用了类这个概念,因而绑定方法我们早就接触过
  • 常用的数据类型 : list, dict, int, float, tuple, set, bool等等
list1 = list([1,2,3])
dict1  = dict({"name":"jack"})
int1 = int("12311321")
float1 = float("11.3")
tuple1 = tuple((1,2,3))
set1 = set(list1)
bool1 = True

print(type(list1))   # <class 'list'>
print(type(dict1))   # <class 'dict'>
print(type(int1))    # <class 'int'>
print(type(float1))  # <class 'float'>
print(type(tuple1))  # <class 'tuple'>
print(type(set1))    # <class 'set'>
print(type(bool1))   # <class 'bool'>

2.list 类演示

#使用"list"类实例化出一个对象
list1 = list([1,2,3,4])
list2 = list([5,6,6,8])

#每个对象都可以使用"list"类的公共属性和方法,我们称之为绑定方法,并且绑定给不同的对象, 内存地址也不相同
print(list1.append)  # <built-in method append of list object at 0x000001A950EAF108>
print(list2.append)  # <built-in method append of list object at 0x000001A950EAF188>

# 每个实例化出来的对象都是独立的个体, 因此我们往list1对象中添加值, 并不会影响到li2
list1.append(4)
print(list1)  # [1, 2, 3, 4]
print(list2)  # [1, 2, 3]

# Python3中"类"与"类型"是一个概念
print(type(list1))  # <class 'list'>
print(type(list2))  # <class 'list'>

六.小结

  • 在上述介绍类与对象的使用过程中,我们更多的是站在底层原理的角度去介绍类与对象之间的关联关系
  • 如果只是站在使用的角度,我们无需考虑语法“对象.属性"中”属性“到底源自于哪里,只需要知道是通过对象获取到的就可以了
  • 所以说,对象是一个高度整合的产物,有了对象,我们只需要使用”对象.xxx“的语法就可以得到跟这个对象相关的所有数据与功能,十分方便且解耦合程度极高
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾维斯Echo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值