python进阶篇-面向对象

1.对象的定义

1.1 什么是对象

面向过程:将程序流程化

对象:就是“容器“,是用来存储数据和功能的,是数据和功能的集合体

面向对象和面向过程没有优劣之分,它们只是使用的场景不同罢了。

1.2 为什么要有对象

有了对象之后,数据和功能之间有关系的会被放在同一个”容器里面“,它的解耦合程度会非常高,方便阅读和维护。

1.3 怎么造对象

如果一个模块里面存放了数据和功能,那么这个模块就是容器,可以被称之为对象。对象的本质就是存放数据和功能的”容器“。我们只要能做到这一点,我们就是在使用面向对象的思想在写程序。但是使用文件整合的话,文件就太多了。

2. 类

2.1 类的概念

类的一方面是将不同的对象做区分,归类的

为了解决不同的对象存储相同数据的问题。所以不仅仅对象是”容器“,类也是”容器“。类用来存放同类对象的他们共有的数据和功能的。所以类也是对象。

将同类对象共有的数据提取到类里面,不是说私有的数据变为共有的了。本质上还是它们自己的。这样做的目的,仅仅是为了归类和节省空间。

在查找数据时,先找对象本身的数据,没有才会到类里面去找。

88d4da0090f347e6875c47dc20ff3df5.png

2.2 类怎么定义

类名采用驼峰体命名法,首字母大写

类的子代码在定义阶段就会直接运行。所以类的名称空间在定义阶段就会产生

aa5467c33c404ec1a2befc7634dff409.png

2.3 类的属性访问

Hero.__dict__会得到一个字典,这个字典里面存放的就是类名称空间里面的名字

9c229bf6f83e484bb857b07f32c7ea32.png

可以使用字典取值的方式,来获取类的属性和方法 

cc13e90866664206b1ffd351e51f71d1.png

也可以通过类名.属性名的方式来对类的属性进行访问,但是本质上就是通过字典取值。这样只不过是python做的优化罢了。 

3.对象

3.1 对象的产生

类造出来之后,他本质上是为了将所有的同类对象提取出来。类里面存放的东西只是为了减少计算机资源的浪费,本质上还是属于具体对象的。所以将数据和功能提取到类里面之后还没有结束,还是需要建立对象和类之间的关联。让类顺着这个关联,还可以访问到原来属于它那部分的数据和功能。

只要基于类加括号就会创造一个对象,并且会将类和对象之间建好关联。调好之后就会返回值就是一个英雄的对象。

类加括号的调用,并不会触发类子代码的运行。类的子代码在定义阶段就已经执行过了。调用的时候只会帮我们造好一个对象,然后建立好类与对象之间的关联,并不会执行类的子代码。调用一次就会帮我们创建一个对象。

对象里面也存在一个__dict__,里面存放的就是对象的属性。现在三个字典里面都是空的。因为我们只是基于类创建了对象,并没有往对象里面添加内容。但是并不意味着这些对象里面没有任何属性,因为类里面有。

e634a5deb73f447d98f2022903cab7a0.png

3ff7107f80a8496884b74e9fbed2c5b7.png

3.2 对象属性的添加

 通过对象.属性的方式来增加,修改,访问属性,本质上都是通过字典的方式来操作属性的

hero1_obj.name = '鲁班七号'
hero1_obj.speed = 450
hero1_obj.hp = 3000
hero1_obj.atk = 100
print(hero1_obj.__dict__)
print(hero1_obj.hero_work)

hero2_obj.name = '后羿'
hero2_obj.speed = 460
hero2_obj.hp = 3100
hero2_obj.atk = 110
print(hero2_obj.__dict__)
print(hero2_obj.hero_work)

hero3_obj.name = '虞姬'
hero3_obj.speed = 470
hero3_obj.hp = 3200
hero3_obj.atk = 120
print(hero3_obj.__dict__)
print(hero3_obj.hero_work)

97a4382c836b44fa94ba19c216061bf7.png

问题:这样就出现了一个问题,给对象添加属性的过程都是一样的并且重复的,太过烦琐了。

3.3 __init__方法

只要我们在类里面定义了__init__方法,这个方法就会在类被调用的时候,自动执行

python在调用类创建对象的时候,会自动调用类下面的__init__,并且将创建的对象自动传递进来(现在这个对象还是空的,没有任何自己独有的属性)

调用类的过程

  • 创建空对象
  • 调用__init__方法,同时将空对象,以及调用类时候括号里面传递的参数,一同传递给__init__方法。
  • 返回初始化之后的对象(注意这个对象并不是__init__返回的,返回对象是类的底层做的一些事情)
class Hero:
    hero_work = '射手'
    count = 0

    def __init__(self, name, speed, hp, atk):
        self.name = name
        self.speed = speed
        self.hp = hp
        self.atk = atk
        self.equipment = []
        Hero.count += 1

    def get_hero_info(self):
        print(f'英雄属性:名字:{self.name} 移速:{self.speed} '
              f'生命值:{self.hp} 攻击力:{self.atk}')

    def set_hero_speed(self, speed_plus):
        self.speed += speed_plus

    def buy_equipment(self, e_name):
        self.equipment.append(e_name)


# 实例化
hero1_obj = Hero('鲁班七号', 450, 3000, 100)  # Hero.__init__(空对象,)
hero2_obj = Hero('后羿', 460, 3100, 110)
hero3_obj = Hero('虞姬', 470, 3200, 120)

3.4 属性查找顺序

我们现在调用类产生对象,就会产生对象自己的名称空间,里面放的也是对象的独有属性.

类里面放的是对象的共有属性

对象查找属性的时候,一定是先在自己的名称空间里面找,里面没有,才回到它所属类的名称空间里面找.

# _*_ coding utf-8 _*_
# george
# time: 2024/9/19下午5:28
# name: attribution.py
# comment:
class Hero:
    hero_work = '射手'
    count = 0

    def __init__(self, name, speed, hp, atk):
        self.name = name
        self.speed = speed
        self.hp = hp
        self.atk = atk
        self.equipment = []
        Hero.count += 1

    def get_hero_info(self):
        print(f'英雄属性:名字:{self.name} 移速:{self.speed} '
              f'生命值:{self.hp} 攻击力:{self.atk}')

    def set_hero_speed(self, speed_plus):
        self.speed += speed_plus

    def buy_equipment(self, e_name):
        self.equipment.append(e_name)


# 实例化
hero1_obj = Hero('鲁班七号', 450, 3000, 100)  # Hero.__init__(空对象,)
hero2_obj = Hero('后羿', 460, 3100, 110)
hero3_obj = Hero('虞姬', 470, 3200, 120)

hero3_obj.hero_work = "法师"

print(Hero.hero_work)
print(hero1_obj.hero_work)
print(hero2_obj.hero_work)
print(hero3_obj.hero_work)

8bb1a4771f584764906954ff5f81c5de.png

3.5 数据属性特点

我们只要修改了类里面的数据属性,通过这个类实例化的所有对象,都是可以感知到这个变化的.

需求:每次实例化一个对象,count就+1

通过Hero.count来修改类的属性之后,所有的对象都可以感知此变化

# _*_ coding utf-8 _*_
# george
# time: 2024/9/19下午5:28
# name: attribution.py
# comment:
class Hero:
    hero_work = '射手'
    count = 0

    def __init__(self, name, speed, hp, atk):
        self.name = name
        self.speed = speed
        self.hp = hp
        self.atk = atk
        self.equipment = []
        Hero.count += 1

    def get_hero_info(self):
        print(f'英雄属性:名字:{self.name} 移速:{self.speed} '
              f'生命值:{self.hp} 攻击力:{self.atk}')

    def set_hero_speed(self, speed_plus):
        self.speed += speed_plus

    def buy_equipment(self, e_name):
        self.equipment.append(e_name)


# 实例化
hero1_obj = Hero('鲁班七号', 450, 3000, 100)  # Hero.__init__(空对象,)
hero2_obj = Hero('后羿', 460, 3100, 110)
hero3_obj = Hero('虞姬', 470, 3200, 120)

hero3_obj.hero_work = "法师"

print(Hero.hero_work)
print(hero1_obj.hero_work)
print(hero2_obj.hero_work)
print(hero3_obj.hero_work)

3.6 绑定方法

3.6.1 类的函数属性

类的数据属性是共享给对象使用的,只要类的数据属性发生变化,对象是能够立马感知到的

对于类的函数属性,类本身是可以使用的.通过类来调用,要严格按照函数的用法来,有几个参数传递几个参数.

be24a6ee902f468d89f3ef0590644812.png

3.6.2 对象调用类的函数属性 

类里面定义的函数,主要是给对象去用的,而且是绑定给对象使用的,虽然所有的对象都是指向相同的功能,但是绑定到不同的对象,就会变成不同的绑定方法

73fc4bcf113348b597745100766f92d9.png

类的函数属性绑定给对象使用之后,就不再是普通的函数,而是绑定方法.我们使用谁来调用绑定方法,绑定方法就会把水作为第一个参数传递进去.我们在实例化对象的时候,自动触发的也是对象的init绑定方法,所以也不需要传递对象本身. 

3.6.3  类函数的形参个数

正是因为有绑定方法的存在,所以我们在类里面定义函数的时候,就至少需要一个形参.即便你的函数里面不需要任何参数.因为我们在通过对象调用这个函数的时候,绑定方法会将对象传递进来,所以需要一个形参来接收绑定方法传进来的对象.

3.6.4 类函数的self

从规范上面来说,第一个参数我们应该写成self,这个self没有任何特殊的地方,仅仅是一个变量名罢了

3.6.5 快捷键

快捷键:alt+j,按一次就会选中一个相同变量名

1f7181f56a6c45b6bacca16a9686cca3.png

3.6.6 绑定方法的方便之处

这就是绑定方法厉害的地方,谁调用的绑定方法处理的就是谁的数据.

# _*_ coding utf-8 _*_
# george
# time: 2024/9/19下午5:28
# name: attribution.py
# comment:
class Hero:
    hero_work = '射手'
    count = 0

    def __init__(self, name, speed, hp, atk):
        self.name = name
        self.speed = speed
        self.hp = hp
        self.atk = atk
        self.equipment = []
        Hero.count += 1

    def get_hero_info(self):
        print(f'英雄属性:名字:{self.name} 移速:{self.speed} '
              f'生命值:{self.hp} 攻击力:{self.atk}')

    def set_hero_speed(self, speed_plus):
        self.speed += speed_plus

    def buy_equipment(self,e_mp):
        self.equipment.append(e_mp)


# 实例化
hero1_obj = Hero('鲁班七号', 450, 3000, 100)  # Hero.__init__(空对象,)
hero2_obj = Hero('后羿', 460, 3100, 110)
hero3_obj = Hero('虞姬', 470, 3200, 120)

hero1_obj.buy_equipment("末世")
hero2_obj.buy_equipment("大棒")
hero3_obj.buy_equipment("复活甲")
print(hero1_obj.equipment)
print(hero2_obj.equipment)
print(hero3_obj.equipment)

f9d592bc5a274005ae27ae9d0166075a.png

3.6.7 python一切皆对象

基本数据类型也是类

我们定义好一个字符串,一个列表,字典的时候,其实就是在造对象.我们通过造出来的对象.他们的方法,其实就是对象在调用它们的绑定方法

de141c240e1043bdae209374a22317a2.png

都是存在self的 

bc8945dfbfa0483a9f2bc74902a966a1.png

x= "aaa",其实背后是触发了一个功能叫做x = str("aaa"),这其实就是类的实例化的过程,x就是通过str实例化出来的一个对象.只不过是python给我们优化了基本数据类型的实例化过程.直接使用"",就可以造出字符串对象. 

a0f36845d31d41faac2c2bb089999102.png

4.面向对象三大属性

面向对象的三大属性,封装,继承和多态

4.1 封装

封装是面向对象最核心的一个特性,封装就是将一堆东西放到容器里面,然后封起来,也就是前面一直在说的整合 

4.1.1 隐藏属性

特点:

  • 隐藏的本质,只是一种改名操作
  • 对外不对内
  • 这个改名操作,只会在类的定义阶段检查类的子代码的时候执行一次,之后定义的__开头的属性都不会改名

我们封装的时候可以将属性隐藏起来,让使用者没有办法直接使用,而不是让使用者不能使用。

我们无论是隐藏数据属性还是函数属性,直接在变量名前面加__就可以实现。

class Test:
    __x = 10

    def __f1(self):
        print("f1")
print(Test.x)

dad7011500d14cb7be1f90c1176b088d.png

我们只要在属性前面加上__,在类的定义阶段,__开头的属性名字就会被加上前缀_ 

d682eb67d8354a3db6dd6b6eac539a75.png 所以__x =》_Test__x,__f1=>_Test_f1.所以pyhton的这种隐藏机制,并没有做到真正意义上的隐藏,其实就是一种改名操作,我们其实只要知道了类名和属性名,就可以拼接出变形之后的名字,然后对她进行访问

e5e73478b10e465dbb597c26a537a1db.png

对外不对内的含义就是,我们在类的外部通过__x是访问不到隐藏属性的,但是在类的内部通过__x和__f1是可以访问到的。

class Test:
    __x = 10

    def __f1(self):
        print("f1")

    def f2(self):
        print(self.__x)
        print(self.__f1)


obj = Test()
obj.f2()

8e1a704a94f54eae93d50d72f3e2d78b.png

为什么在类的外部不可以访问隐藏属性,在类的内部却是可以访问的呢? 

在类的定义阶段,检查类子代码语法的时候,对类内部__开头的名字统一进行了改名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值