第二十七篇 类和对象相关知识
Posted victorm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二十七篇 类和对象相关知识相关的知识,希望对你有一定的参考价值。
类和对象
1. 什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类食物(食物即数据和动作的结合体),用它来生产真是的物体(实例)
2. 什么叫对象:睁开眼,你看到的一切事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)
(铅笔是对象,人是对象,房子是对象,狗是对象,你看到的都是类)
3.类与对象的关系:对象都是由类产生的,女娲造人,首先由一个造人的模板,这个模板就是人类,然后女娲根据类的定义来生产一个个的人
4. 什么叫实例化:由类生产对象的过程叫实例化,类实例化的结构就是一个对象,或者叫做一个实例(实例=对象)
实例:就是类生产的那个对象。是一个实实在在的存在。放到真实世界中,人类就是一个类,这人类是看不见,摸不着的,是人为给划分的;而人,比如,你,我,他,路人等就是一个个的实例,是真实存在,能看得见,摸得着的。
类相关的知识
-
声明类
在Python中,声明函数和声明类很相似:
声明函数
def function(args): ‘‘‘文档字符串‘‘‘ 函数体
声明类
class 类名: ‘‘‘类的文档字符串‘‘‘ 类体
class Chinese: # 声明类,类名的规范:类名的首字母要大写 ‘‘‘这是一个中国人的类‘‘‘ pass print(Chinese) # <class ‘__main__.Chinese‘>
class Chinese: ‘‘‘这是一个中国人的类‘‘‘ pass # 实例化 #实例化,对类名加括号,加括号就代表运行,运行类也有返回值,这个返回值就是一个具体的示例了 p1=Chinese() print(p1) # <__main__.Chinese object at 0x00402EF0> #实例化到底干了什么?后面给你答案
经典类与新式类
经典类:上面的两个示例就是经典类,经典类的特点就是,类名后面直接就是冒号(Python2中)
新式类:都要在类名后面加个括号,括号里写上object
# 新式类:都要在类名后面加个括号,括号里写上object # 意思是Chinese这类继承与object class 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‘: <function Chinese.huang_pi_fu at 0x00C01AE0>, ‘cha_dui‘: <function Chinese.cha_dui at 0x00C01A50>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>}
知道了存放地点,就可以一级级查找下去,访问类的属性啦
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__) # Chinese print(Chinese.__doc__) # 这是一个中国人的类 print(Chinese.__base__) # <class ‘object‘> print(Chinese.__bases__) # (<class ‘object‘>,) print(Chinese.__dict__) # 上面已经有了 print(Chinese.__module__) #__main__ print(Chinese.__class__) #<class ‘type‘>
对象相关的知识
对象:是由类实例化而来的。
- 构造初始化方法及实例化
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__‘: <function Chinese.__init__ at 0x00AB1B70>, ‘sui_di_tu_tan‘: <function Chinese.sui_di_tu_tan at 0x00AB1B28>, ‘cha_dui‘: <function Chinese.cha_dui at 0x00AB1AE0>, ‘eat_food‘: <function Chinese.eat_food at 0x00AB1A50>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>}
小总结:
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: pass x = 10 y = 20 MyData.x = 1 MyData.y = 2 print(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) # <function Chinese.play_ball at 0x009F1AE0>
-
- 增加类的属性
#增加类的数据属性 Chinese.dang = ‘Gong Chandang‘ print(Chinese.dang) # Gong Chandang # 增加类的函数属性 # 首先,要在类的外部写一个函数 def say_word(self,word): return "%s 说 %s" %(self.name, word) # 其次,增加函数属性,将上面的函数赋值给类的函数属性Say_Language Chinese.Say_Language = say_word print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Chinese.__init__ at 0x00C01B28>, ‘play_ball‘: <function Chinese.play_ball at 0x00C01AE0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None, ‘Say_Language‘: <function say_word at 0x00C01B70>}
- 修改类的属性
#修改类的数据属性 Chinese.country="新西兰" print(Chinese.country) # 新西兰 # 类属性被修改了,那实例可以使用吗?可以用的 print(p1.country) # 新西兰 # 修改类的函数属性 # 首先要定义个要修改的目标函数 def play_what(self,what): return "%s 正在玩 %s" %(self.name, what) # 修改类的函数属性 Chinese.play_ball = play_what print(Chinese.__dict__) # 调用类的函数属性 print(p1.play_ball("钢琴")) # alex 正在玩 钢琴
-
- 删除类的属性
# 删除类的数据属性 del Chinese.country print(p1.country) # AttributeError: ‘Chinese‘ object has no attribute ‘country‘ # 删除类的函数属性 del Chinese.play_ball print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Chinese.__init__ at 0x01331B28>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None} # 调用类函数属性 p1.play_ball("足球") # AttributeError: ‘Chinese‘ object has no attribute ‘play_ball‘ # 因为,已经被删除掉了
- 实例属性的使用----增删改查
- 查看实例的属性
# 查看实例的数据属性 print(p1.name) # alex # 查看实例类属性(查看实例的函数属性,实际上是访问的类的函数属性) print(p1.play_ball) # <bound method Chinese.play_ball of <__main__.Chinese object at 0x00D22ED0>>
# 运行,函数属性后加()就变成运行了。
print(p1.play_ball(‘篮球‘))
-
- 增加实例的属性
# 增加实例的数据属性 p1.age=18 print(p1.__dict__) # {‘name‘: ‘alex‘, ‘age‘: 18} print(p1.age) # 18 #备注,无法增加实例的函数属性哦,因为函数属性是通过类增加的 # 增加实例的函数属性(虽然可以增加,但是没有这么玩的,要知道可以增加,但要迅速忘记它,千万别这么用,纯属多余的) def shili_shuxing(self): return "我是来自实例的函数属性" p1.Shi_Li_De_Shu_Xing = shili_shuxing print(p1.__dict__) # {‘name‘: ‘alex‘, ‘Shi_Li_De_Shu_Xing‘: <function shili_shuxing at 0x00D61AE0>} print(p1.Shi_Li_De_Shu_Xing(p1)) # 我是来自实例的函数属性
- 修改实例的属性
# 修改实例的数据属性 p1.age=19 print(p1.__dict__) print(p1.age) # 结果 {‘name‘: ‘alex‘, ‘age‘: 19} 19
-
- 删除实例的属性
#删除实例的数据属性 del p1.age print(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里新增了一个country p1.country=‘日本‘ print(‘类的--->‘,Chinese.country) # China print(‘实例的‘,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) # China p1.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‘]} # 这其实就是通过实力引用类的属性,并直接操作了类的属性
以上是关于第二十七篇 类和对象相关知识的主要内容,如果未能解决你的问题,请参考以下文章