类和对象
1. 什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类食物(食物即数据和动作的结合体),用它来生产真是的物体(实例)
2. 什么叫对象:睁开眼,你看到的一切事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)
(铅笔是对象,人是对象,房子是对象,狗是对象,你看到的都是类)
3.类与对象的关系:对象都是由类产生的,女娲造人,首先由一个造人的模板,这个模板就是人类,然后女娲根据类的定义来生产一个个的人
4. 什么叫实例化:由类生产对象的过程叫实例化,类实例化的结构就是一个对象,或者叫做一个实例(实例=对象)
实例:就是类生产的那个对象。是一个实实在在的存在。放到真实世界中,人类就是一个类,这人类是看不见,摸不着的,是人为给划分的;而人,比如,你,我,他,路人等就是一个个的实例,是真实存在,能看得见,摸得着的。
类相关的知识
-
声明类
在Python中,声明函数和声明类很相似:
声明函数
def function(args): '''文档字符串''' 函数体
声明类
class 类名: '''类的文档字符串''' 类体
class Chinese: # 声明类,类名的规范:类名的首字母要大写 '''这是一个中国人的类''' passprint(Chinese) #
class Chinese: '''这是一个中国人的类''' pass# 实例化#实例化,对类名加括号,加括号就代表运行,运行类也有返回值,这个返回值就是一个具体的示例了p1=Chinese()print(p1)# <__main__.Chinese object at 0x00402EF0>#实例化到底干了什么?后面给你答案
经典类与新式类
经典类:上面的两个示例就是经典类,经典类的特点就是,类名后面直接就是冒号(Python2中)
新式类:都要在类名后面加个括号,括号里写上object
# 新式类:都要在类名后面加个括号,括号里写上object# 意思是Chinese这类继承与objectclass Chinese(object): pass
注意,Python3中统一都是新式类,没有区别了,类名后面是否加(object),都是新式类。
-
属性
类是用来描述一类事物的,类的对象指的是这一类事物中的一个个体。
既然是事物,那就要有属性,属性分为:
1. 数据属性:就是变量 (就是第二十五篇里讲的特征)
2. 函数属性:就是函数,在面向对象里通常称为方法 (就是第二十五篇里讲的动作)
注意:
类和对象均用点来访问自己的属性
-
类的属性
数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用。
class Chinese: '这是一个中国人的类' # 类的数据属性 party='渣滓洞' # 类的函数属性,就是方法 def huang_pi_fu(): print('中国人都是黄皮肤') def cha_dui(self): print('%s插到了前面' %self)
-
访问类的属性(查看类属性)
要访问类的属性,你首先得知道类的属性在哪里吧?就好比你要逛亲戚,你得知道亲戚家在哪里吧,然后才能选择用什么交通工具到你亲戚家。
那要去你亲戚家,你怎么知道路呢?可以问爸妈,也可以给亲戚打电话,还可以让亲戚发定位等等。
同理,定义了类的属性,也要知道存放在哪里了?有两种方式可以查看
#第一种:dir(类名): 得到的是一个名字列表,只放了属性的名字在列表里print(dir(Chinese))# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cha_dui', 'huang_pi_fu', 'party']# 第二种:类名.__dict__ :查出的是一个字典,key为属性名,value为属性值 __dict__: 查看类的属性字典
print(Chinese.__dict__)# {'__module__': '__main__', '__doc__': '这是一个中国人的类', 'party': '渣滓洞', 'huang_pi_fu':, 'cha_dui': , '__dict__': , '__weakref__': }
知道了存放地点,就可以一级级查找下去,访问类的属性啦
print(Chinese.__dict__['party']) # 渣滓洞print(Chinese.__dict__['huang_pi_fu']()) # 中国人都是黄皮肤print(Chinese.__dict__['cha_dui']('汪汪队')) # 汪汪队插到了前面
Python提供了更简单的方法:
类名.属性名:就可以访问到属性。(本质上就是在查询属性字典)
# 访问类的数据属性,通过点来访问print(Chinese.party) # 渣滓洞# 访问类的函数属性,通过点来访问Chinese.huang_pi_fu() # 中国人都是黄皮肤Chinese.cha_dui('dig') # dig插到了前面
-
特殊的类属性
#python为类内置的特殊属性类名.__name__# 类的名字(字符串)类名.__doc__# 类的文档字符串类名.__base__# 类的第一个父类(在讲继承时会讲)类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)类名.__dict__# 类的字典属性类名.__module__# 类定义所在的模块类名.__class__# 实例对应的类(仅新式类中)类的特殊属性(了解即可)
print(Chinese.__name__) # Chineseprint(Chinese.__doc__) # 这是一个中国人的类print(Chinese.__base__) #print(Chinese.__bases__) # ( ,)print(Chinese.__dict__) # 上面已经有了print(Chinese.__module__) #__main__print(Chinese.__class__) #
对象相关的知识
对象:是由类实例化而来的。
- 构造初始化方法及实例化
class Chinese: '这是一个中国人的类' party='渣滓洞' ''' 首先,在类定义当中,要有一个初始化函数来帮我们定制每一个对象的属性 其次,类class的语法结构当中,提供了内置方法,只要你把初始化函数定义为__init__()这个名字,你在用类名()来运行你的类的时候,他就会自动的找到 __init__来帮你去运行 第三,__init__()必须要有一个self参数,然后再接着写其他数据属性(变量参数) 第四, 定义数据属性时,self.mingzi = name,代表给这个self这个实例赋予了一个mignzi属性,mingzi属性就是数据属性;name就是执行__init()函数传进来的参数 这样就把名字,年龄,性别统统都封装到了self这个实例自己里面了 最后,return返回了一个self实例,但是不用显示的return,而是class自动给你return了,最后返回的结果就是一个字典。 字典里封装了什么?封装了名字,年龄,性别这些数据属性 ''' # 1. 初始化构造方法 def __init__(self,name,age,gender): print('我是初始化函数,我开始运行了') # self:就是实例自己 self.mingzi=name # 相当于 p1.mingzi=name self.nianji=age # 相当于 p1.nianji=age self.xingbie=gender # 相当于 p1.xingbie = gender print('我结束啦')# 上面已经构造好了初始化一个实例的方法,下面就来真真的生成一个实例对象# 具体的实例化的过程:# 1. 实例化的过程,本质上就是调用并运行了一次__init__(self,name,age,gender)函数,# 2. 在调用__init__的过程中,self其实就是p1,在运行过程中,会自动的将p1传给self,然后'元昊',18,'female'分别传给name,age,gender# 3. 其实就是Chinese.__init__(p1,name,age,gender) # 2. 实例化:实际就是生成了一个真实存在的对象。 p1 = Chinese('小黄',18,'female')
# 我是初始化函数,我开始运行了 # 我结束啦
# 上面初始化构造方法里面已经讲了,初始化构造返回的实例就是一个数据字典,那我们来看看到底是不是 print(p1) # <__main__.Chinese object at 0x00C39890> print(p1.__dict__) # {'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}
- 实例调用属性
# 上面已经实例化了一个p1,p1就是真实存在的一个人# 就可以查看p1这个人具备哪些属性?# 首先,他具有这个类所共有的属性,还可以有他自己特有的属性print(p1.mingzi) # 小黄# p1.mingzi,作用域在__init__里,可以调用到,很正常# 那p1.dang首先在__init__作用域里没找到,就会向外一层找,就有dang这个属性啦,所以可以调用了print(p1.dang) # 渣滓洞# 查看p1的属性print(p1.__dict__)# {'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}# 查看类的属性print(Chinese.__dict__)# {'__module__': '__main__', '__doc__': '这是一个中国人的类', 'dang': '渣滓洞', '__init__':, 'sui_di_tu_tan': , 'cha_dui': , 'eat_food': , '__dict__': , '__weakref__': }
小总结:
1. 实例是怎么产生的?
【答】实例的产生,就是执行了一个初始化方法__init__()产生的。__init__()返回的就是一个实例属性字典。实例属性字典里就是没有函数属性,并不包含函数属性
2. 函数属性是属于类的,从层级关系上也能看出来。
- 构造函数属性
class Chinese: '这是一个中国人的类' dang='渣滓洞' # 构造了初始化函数 def __init__(self,name,age,gender): self.mingzi=name # 相当于 p1.mingzi=name self.nianji=age # 相当于 p1.nianji=age self.xingbie=gender # 相当于 p1.xingbie = gender # 构造函数属性 def sui_di_tu_tan(self): print('%s 朝着墙上就是一口痰' %self.mingzi) def cha_dui(self): print(self) print('%s 插到了前面' %self.mingzi) def eat_food(self,food): print('%s 正在吃%s' %(self.mingzi,food))
- 实例调用函数属性
# 实例调方法的顺序# 1. 先从自己的字典里找 p1自己的字典:{'mingzi': '元昊', 'nianji': 18, 'xingbie': 'female'}# 2. 再到类的字典里面去找, 类的字典:Chinese.__dict__# 能找到就可以运行p1.sui_di_tu_tan()# 小黄 朝着墙上就是一口痰# 问题:sui_di_tu_tan(self)定义的时候有个self参数,那为什么这里运行的时候,没有传参数,也能正常运行呢?
- 关于self的总结
1、self 就代表自己,自己就是实例化的结果p1。谁来实例化,self就是谁。
2、为什么要有self?self就是来做统一的3、只要定义了self,一执行函数,就会自动把p1传给函数的第一个参数。4、所以,以后只要碰到self,就要知道self就是实例本身
来一张图
有人说,类有数据属性和函数属性,实例/对象 是由类产生的,所以实例也有数据属性和函数属性了------这是错的哇,记住啦。
因为:
实例化的过程实际就是执行__init__的过程,这个__init__函数内部只是为实例本身即self设定了一堆数据(变量),所以实例只有数据属性;它的所谓的函数属性是从类里找来的而已。
还有人说,实例是类产生的,所以实例肯定能访问到类属性,然后就没有说为什么了------这也是错的哇。
因为:
1. 首先你会发现,实例化就是 类名(),然后返回的结果是一个对象,加上括号是不是跟函数运行很像,函数运行完了有返回值,很像那
2. 函数有作用域的概念,其实类也有作用域的概念,二者一样
3. 你可以吧class当做最外层的函数,是一个作用域
# 定义一个类,只当一个作用域用class MyData: passx = 10y = 20MyData.x = 1MyData.y = 2print(x, y)print(MyData.x, MyData.y)print(MyData.x + MyData.y)
4. 实例化会自动触发__init__函数的运行,最后返回一个值即实例,我们要找的实例属性就存放在__init__函数的局部作用域里
5. 类有类的属性字典,就是类的作用域,实例有实例的属性字典,就是实例的作用域
6. 综上,一个点代表一层作用域,obj.x 先从自己的作用域找,自己找不到再去外出的类的字典中找,都找不到,就会报错
7. 在类中没有使用点的调用,代表调用全局变量。
所以,上面的说法犯了因果倒置的错误。不是因为实例是由类产生的,所以实例才能访问到类属性。
上面这个只是为了帮助理解,而不能真的这么说。
茅塞顿开,总结一句话就是:
类有个属性字典,实例也有个属性字典;
查的时候,实例/对象先从自己的属性字典里查,再到类的属性字典里去查整理下上面的代码
class Chinese: '这是一个中国人的类' dang='渣滓洞' def __init__(self,name,age,gender): self.mingzi=name # 相当于 p1.mingzi=name self.nianji=age self.xingbie=gender def sui_di_tu_tan(self): print('%s 朝着墙上就是一口痰' %self.mingzi) def cha_dui(self): print(self) print('%s 插到了前面' %self.mingzi) def eat_food(self,food): print('%s 正在吃%s' %(self.mingzi,food))# 实例化可以生成多个对象p1=Chinese('小黄',18,'female')# 对象调用吃的方法p1.eat_food('包子') # 小黄 正在吃包子p2=Chinese('武sir',23,'姑娘')# 对象也可以调用吃的方法p2.eat_food('韭菜馅饼') 武sir 正在吃韭菜馅饼
- 属性有两种:数据属性和函数属性
- 类属性的使用----增删改查
class Chinese: country='China' def __init__(self,name): self.name=name # 函数属性的定义原则:动词_名词。 干什么事 def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball)) # 下面的函数定义与类是同级的,与类没半毛钱关系。def say_word(self,word): return "%s 说 %s" %(self.name, word)# 实例化一个实例p1=Chinese('alex')# print(p1.__dict__) # {'name': 'alex'}
-
- 查看类的属性
#查看类的数据属性:用点的方式查。 print(Chinese.country) # China# 查看类的函数属性print(Chinese.play_ball) #
-
- 增加类的属性
#增加类的数据属性Chinese.dang = 'Gong Chandang'print(Chinese.dang) # Gong Chandang# 增加类的函数属性# 首先,要在类的外部写一个函数def say_word(self,word): return "%s 说 %s" %(self.name, word)# 其次,增加函数属性,将上面的函数赋值给类的函数属性Say_LanguageChinese.Say_Language = say_wordprint(Chinese.__dict__)# {'__module__': '__main__', 'country': 'China', '__init__':, 'play_ball': , '__dict__': , '__weakref__': , '__doc__': None, 'Say_Language': }
-
- 修改类的属性
#修改类的数据属性Chinese.country="新西兰"print(Chinese.country) # 新西兰# 类属性被修改了,那实例可以使用吗?可以用的print(p1.country) # 新西兰# 修改类的函数属性# 首先要定义个要修改的目标函数def play_what(self,what): return "%s 正在玩 %s" %(self.name, what)# 修改类的函数属性Chinese.play_ball = play_whatprint(Chinese.__dict__)# 调用类的函数属性print(p1.play_ball("钢琴")) # alex 正在玩 钢琴
-
- 删除类的属性
# 删除类的数据属性del Chinese.countryprint(p1.country)# AttributeError: 'Chinese' object has no attribute 'country'# 删除类的函数属性del Chinese.play_ballprint(Chinese.__dict__)# {'__module__': '__main__', 'country': 'China', '__init__':, '__dict__': , '__weakref__': , '__doc__': None}# 调用类函数属性p1.play_ball("足球")# AttributeError: 'Chinese' object has no attribute 'play_ball'# 因为,已经被删除掉了
- 实例属性的使用----增删改查
- 查看实例的属性
# 查看实例的数据属性 print(p1.name) # alex # 查看实例类属性(查看实例的函数属性,实际上是访问的类的函数属性)print(p1.play_ball) #> # 运行,函数属性后加()就变成运行了。
print(p1.play_ball('篮球'))
-
- 增加实例的属性
# 增加实例的数据属性p1.age=18print(p1.__dict__) # {'name': 'alex', 'age': 18}print(p1.age) # 18#备注,无法增加实例的函数属性哦,因为函数属性是通过类增加的# 增加实例的函数属性(虽然可以增加,但是没有这么玩的,要知道可以增加,但要迅速忘记它,千万别这么用,纯属多余的)def shili_shuxing(self): return "我是来自实例的函数属性"p1.Shi_Li_De_Shu_Xing = shili_shuxingprint(p1.__dict__)# {'name': 'alex', 'Shi_Li_De_Shu_Xing':}print(p1.Shi_Li_De_Shu_Xing(p1))# 我是来自实例的函数属性
-
- 修改实例的属性
# 修改实例的数据属性p1.age=19print(p1.__dict__)print(p1.age)# 结果{ 'name': 'alex', 'age': 19}19
-
- 删除实例的属性
#删除实例的数据属性del p1.ageprint(p1.__dict__)
那,换个姿势,实例和类混合着玩会怎么样?
# 示例代码class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')print(p1.country) # China 访问的是类的# 在p1里新增了一个countryp1.country='日本'print('类的--->',Chinese.country) # Chinaprint('实例的',p1.country) # 日本
# 作用域问题class Chinese: def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')print(p1.country)# AttributeError: 'Chinese' object has no attribute 'country'# 原因, 因为类里没有country属性# 那如果把country定义到类外面那?country='中国'class Chinese: def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')print(p1.country)# AttributeError: 'Chinese' object has no attribute 'country'# 原因:作用域只在类里面找,放到类外部照样找不到
class Chinese: def __init__(self): # 在实例化过程中接受输入参数 name = input("请输入用户名: ") self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese()print(p1.name)# 备注:'''虽然这么做是可以的,但是千万别这么干。因为一个函数就是实现一个功能,而这么干,还实现了接受用户输入的功能。可读性差,也不利于维护'''# 可以改进成如下方式,专门写一个函数来处理接受输入和实例化的过程,代码变为:class Chinese: def __init__(self, name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))def shi_li_hua(): name=input('>>: ') # 接收输入 p1=Chinese(name) # 实例化 print(p1.country) # 调用 print(p1.name) # 调用shi_li_hua() # 直接调用运行
在类中没有使用点的调用,代表调用全局变量。
country='中国'class Chinese: def __init__(self,name): self.name=name print('--->',country) def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')# ---> 中国# 为什么?# 其实前面已经给了答案了。所说的在类里面找属性,是通过点这种方式才表明这个属性是需要再类里面去找的,而此时,country并没有通过点的方式,所以它就是一个普通的变量名,即使这个变量在类的外面,照样也可以使用
country='-------------中国-------------'class Chinese: country='中国' def __init__(self,name): self.name=name print('--->',country) def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1 = Chinese("alex")# ---> -------------中国-------------# 原因:# 遵循在类中没有使用点的调用,代表调用全局变量。
class Chinese: country='China' def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')print(p1.country) # Chinap1.country='Japan' # 改的只是p1自己的国家print(Chinese.country) # China 类的数据属性还是没有被改掉的
class Chinese: country='China' l=['a','b'] def __init__(self,name): self.name=name def play_ball(self,ball): print('%s 正在打 %s' %(self.name,ball))p1=Chinese('alex')print(p1.l) # ['a', 'b']p1.l=[1,2,3] # 给p1新增了一个属性值,所以只会改p1自己的print(p1.l) # [1, 2, 3] 改的仅仅是p1自己的print(Chinese.l) # ['a', 'b'] 类的属性l 是不会变的print(p1.__dict__) # {'name': 'alex', 'l': [1, 2, 3]}# 但是,这种方式就有问题了# 首先,没有给p1新定义属性,# 其次, p1.l调的就是类的,这个l那的就是列表的引用,append操作的就是类p1.l.append('c') print(p1.__dict__) # {'name': 'alex', 'l': [1, 2, 3]}print(Chinese.l) # {'name': 'alex', 'l': [1, 2, 3, 'c']}# 这其实就是通过实力引用类的属性,并直接操作了类的属性