1.对象的定义
1.1 什么是对象
面向过程:将程序流程化
对象:就是“容器“,是用来存储数据和功能的,是数据和功能的集合体。
面向对象和面向过程没有优劣之分,它们只是使用的场景不同罢了。
1.2 为什么要有对象
有了对象之后,数据和功能之间有关系的会被放在同一个”容器里面“,它的解耦合程度会非常高,方便阅读和维护。
1.3 怎么造对象
如果一个模块里面存放了数据和功能,那么这个模块就是容器,可以被称之为对象。对象的本质就是存放数据和功能的”容器“。我们只要能做到这一点,我们就是在使用面向对象的思想在写程序。但是使用文件整合的话,文件就太多了。
2. 类
2.1 类的概念
类的一方面是将不同的对象做区分,归类的
为了解决不同的对象存储相同数据的问题。所以不仅仅对象是”容器“,类也是”容器“。类用来存放同类对象的他们共有的数据和功能的。所以类也是对象。
将同类对象共有的数据提取到类里面,不是说私有的数据变为共有的了。本质上还是它们自己的。这样做的目的,仅仅是为了归类和节省空间。
在查找数据时,先找对象本身的数据,没有才会到类里面去找。
2.2 类怎么定义
类名采用驼峰体命名法,首字母大写
类的子代码在定义阶段就会直接运行。所以类的名称空间在定义阶段就会产生
2.3 类的属性访问
Hero.__dict__会得到一个字典,这个字典里面存放的就是类名称空间里面的名字
可以使用字典取值的方式,来获取类的属性和方法
也可以通过类名.属性名的方式来对类的属性进行访问,但是本质上就是通过字典取值。这样只不过是python做的优化罢了。
3.对象
3.1 对象的产生
类造出来之后,他本质上是为了将所有的同类对象提取出来。类里面存放的东西只是为了减少计算机资源的浪费,本质上还是属于具体对象的。所以将数据和功能提取到类里面之后还没有结束,还是需要建立对象和类之间的关联。让类顺着这个关联,还可以访问到原来属于它那部分的数据和功能。
只要基于类加括号就会创造一个对象,并且会将类和对象之间建好关联。调好之后就会返回值就是一个英雄的对象。
类加括号的调用,并不会触发类子代码的运行。类的子代码在定义阶段就已经执行过了。调用的时候只会帮我们造好一个对象,然后建立好类与对象之间的关联,并不会执行类的子代码。调用一次就会帮我们创建一个对象。
对象里面也存在一个__dict__,里面存放的就是对象的属性。现在三个字典里面都是空的。因为我们只是基于类创建了对象,并没有往对象里面添加内容。但是并不意味着这些对象里面没有任何属性,因为类里面有。
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)
问题:这样就出现了一个问题,给对象添加属性的过程都是一样的并且重复的,太过烦琐了。
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)
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 类的函数属性
类的数据属性是共享给对象使用的,只要类的数据属性发生变化,对象是能够立马感知到的
对于类的函数属性,类本身是可以使用的.通过类来调用,要严格按照函数的用法来,有几个参数传递几个参数.
3.6.2 对象调用类的函数属性
类里面定义的函数,主要是给对象去用的,而且是绑定给对象使用的,虽然所有的对象都是指向相同的功能,但是绑定到不同的对象,就会变成不同的绑定方法
类的函数属性绑定给对象使用之后,就不再是普通的函数,而是绑定方法.我们使用谁来调用绑定方法,绑定方法就会把水作为第一个参数传递进去.我们在实例化对象的时候,自动触发的也是对象的init绑定方法,所以也不需要传递对象本身.
3.6.3 类函数的形参个数
正是因为有绑定方法的存在,所以我们在类里面定义函数的时候,就至少需要一个形参.即便你的函数里面不需要任何参数.因为我们在通过对象调用这个函数的时候,绑定方法会将对象传递进来,所以需要一个形参来接收绑定方法传进来的对象.
3.6.4 类函数的self
从规范上面来说,第一个参数我们应该写成self,这个self没有任何特殊的地方,仅仅是一个变量名罢了
3.6.5 快捷键
快捷键:alt+j,按一次就会选中一个相同变量名
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)
3.6.7 python一切皆对象
基本数据类型也是类
我们定义好一个字符串,一个列表,字典的时候,其实就是在造对象.我们通过造出来的对象.他们的方法,其实就是对象在调用它们的绑定方法
都是存在self的
x= "aaa",其实背后是触发了一个功能叫做x = str("aaa"),这其实就是类的实例化的过程,x就是通过str实例化出来的一个对象.只不过是python给我们优化了基本数据类型的实例化过程.直接使用"",就可以造出字符串对象.
4.面向对象三大属性
面向对象的三大属性,封装,继承和多态
4.1 封装
封装是面向对象最核心的一个特性,封装就是将一堆东西放到容器里面,然后封起来,也就是前面一直在说的整合
4.1.1 隐藏属性
特点:
- 隐藏的本质,只是一种改名操作
- 对外不对内
- 这个改名操作,只会在类的定义阶段检查类的子代码的时候执行一次,之后定义的__开头的属性都不会改名
我们封装的时候可以将属性隐藏起来,让使用者没有办法直接使用,而不是让使用者不能使用。
我们无论是隐藏数据属性还是函数属性,直接在变量名前面加__就可以实现。
class Test:
__x = 10
def __f1(self):
print("f1")
print(Test.x)
我们只要在属性前面加上__,在类的定义阶段,__开头的属性名字就会被加上前缀_
所以__x =》_Test__x,__f1=>_Test_f1.所以pyhton的这种隐藏机制,并没有做到真正意义上的隐藏,其实就是一种改名操作,我们其实只要知道了类名和属性名,就可以拼接出变形之后的名字,然后对她进行访问
对外不对内的含义就是,我们在类的外部通过__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()
为什么在类的外部不可以访问隐藏属性,在类的内部却是可以访问的呢?
在类的定义阶段,检查类子代码语法的时候,对类内部__开头的名字统一进行了改名