拓冰建站拓冰建站
首页 / 资讯中心 / 正文

【Python3基础】10-类与对象

▒ 目录 ▒ 导读本篇知识路线1️⃣ 类和对象类和对象的定义Python 的类和对象相对其它语言增加的额外特性2️⃣ 构造函数和访问控制构造函数访问控制3️⃣ 一切皆对象isinstance 函数type 函数4️⃣ 类的职责要单一 文章小结 导读面向对象不是把函数搬进类而是让数据与操作这些数据的行为形成清楚的边界。本篇讲解类、对象、属性、方法、构造函数和访问控制。我们会从一个简单模型出发看清类负责描述什么、对象保存什么以及实例之间为什么能共享行为却拥有各自的数据。本篇知识路线类设计说明 ├─ 类属性 ├─ 方法 └─ 构造规则 │ 实例化 ├──► 对象 A实例属性 └──► 对象 B实例属性1️⃣ 类和对象类是抽象的模板我们先定义类然后我们就可以用类这个模板创建出很多对象比如我们定义一个 Human 类 然后用 Human 类创建出很多具体的 Human比如如花星星赵日天等等。无论是对于如花星星还是赵日天这些具体的人他们都有一些共同属性变量和行为函数 我们可以把这些属性和行为定义在他们所属的 Human 类中。类和对象的定义Python 使用关键字class 类名或者class 类名()来定义一个类。classHuman:passclassHuman2():passprint(type(Human))# class typeprint(type(Human2))# class type我们定义好了 Human 类就可以根据 Human 类创建出对象创建对象是通过类名和小括号来实现。classHuman():passluren_aHuman()# 用 Human 类定义一个具体的对象 luren_aprint(type(luren_a))# class __main__.Human我们可以用 Human 类定义多个对象注意每个对象都是独立的个体。classHuman():passluren_aHuman()# 用 Human 类定义一个具体的对象 luren_aluren_bHuman()# 用 Human 类定义一个具体的对象 luren_bprint(id(luren_a))print(id(luren_b))我们可以给 Human 类添加变量属性该变量叫做类的成员变量并且可以通过对象访问到成员变量。classHuman():nameruhualuren_aHuman()print(luren_a.name)# 通过对象访问对象的属性我们也可以给 Human 类添加函数方法该函数叫做类的成员函数 但要注意成员函数必须至少有一个参数我们一般给该参数起名为 self当然也可以是其它名字 该参数用来接收调用者本身调用者本身是 Human 类的一个对象。classHuman():nameruhuadefgetname(self):print(id(self))# self 即是 调用者本身print(self.name)luren_aHuman()print(id(luren_a))# 通过 luren_a 对象调用成员函数时Python 解释器会自动把 luren_a 对象本身作为 getname 的第一个实参。luren_a.getname()Python 的类和对象相对其它语言增加的额外特性Python 允许我们动态的给一个对象绑定属性比如给我们的 Human 类的某个对象动态绑定一个 age 属性。classHuman():nameruhualuren_aHuman()luren_a.age18print(luren_a.age)Python 还允许我们动态的给一个对象绑定函数比如给我们的 Human 类的某个对象动态绑定一个 speak 函数。classHuman():nameruhuadefspeak():print(大家好我是 ruhua)luren_aHuman()luren_a.speakspeak luren_a.speak()我们用 Human 类定义好对象后当给某个对象动态的添加属性和方法时这些属性和方法并不作用于其它对象。classHuman():nameruhuadefspeak():print(大家好我是 ruhua)luren_aHuman()luren_bHuman()luren_a.age18luren_a.speakspeakprint(luren_a.age)# okluren_a.speak()# okprint(luren_b.age)# 错误luren_b.speak()# 错误如果我们想让动态绑定的属性和方法作用于所有对象可以直接对类进行动态绑定比如直接对 Human 类进行动态绑定。classHuman():nameruhuadefspeak(self):print(大家好我是self.name)luren_aHuman()luren_bHuman()Human.age18Human.speakspeakprint(luren_a.age)# okluren_a.speak()# okprint(luren_b.age)# okluren_b.speak()# ok2️⃣ 构造函数和访问控制在面向对象编程中构造函数和访问控制是我们经常用到的并且是面向对象很重要的特性。构造函数虽然我们需要类的所有实例拥有共同的属性 但是我们不想让他们拥有共同的属性值在对象创建后我们可以调用一个函数来修改这些属性值。classHuman():definitdata(self,myname,myage):self.namemyname self.agemyagedefspeak(self):print(大家好我是%s我的年龄是%s%(self.name,self.age))luren_aHuman()luren_a.initdata(ruhua,18)luren_a.speak()luren_bHuman()luren_b.initdata(zhaoritian,17)luren_b.speak()我们发现上面那种方法重复繁琐Python 语言可以通过定义一个名称为init的函数该函数叫做构造函数在我们创建对象的时候Python 解释器会自动调用构造函数。构造函数也是类的成员函数至少也要拥有一个参数我们知道第一个参数是用来接收调用对象本身的在构造函数内部我们可以把各种属性绑定到该参数上。classHuman():def__init__(self,myname,myage):self.namemyname self.agemyagedefspeak(self):print(大家好我是%s我的年龄是%s%(self.name,self.age))luren_aHuman(ruhua,18)# Python 解释器会自动调用构造函数并把 luren_a 作为第一个实参传给构造函数luren_a.speak()luren_bHuman(xingxing,17)# Python 解释器会自动调用构造函数并把 luren_b 作为第一个实参传给构造函数luren_b.speak()由于在构造函数中我们把调用构造函数的对象本身传过去然后通过这个对象初始化属性这样一来可以实现每个对象拥有不同的属性值。classHuman():def__init__(self,myname):self.namemyname luren_aHuman(ruhua)luren_bHuman(xingxing)print(luren_a.name)# luren_a 对象的 nameprint(luren_b.name)# luren_b 对象的 name假如我们定义一个圆类对于所有的圆类产生的对象都有 π 这个属性并且该属性对所有圆对象的值都是一样的 我们没必要把 π 这个属性在每个对象创建的时候在构造函数内重新赋值因为他们是一样的我们可以把该属性定义在构造函数外面我们称为该属性是属于类的也就是所有对象共有一个。classCircle():PI3.1415926def__init__(self,radius):self.radiusradiusdefshowarea(self):print(self.radius*self.PI)redcircleCircle(3)redcircle.showarea()# 用的是类的属性 PIbluecircleCircle(5)# 用的是类的属性 PIbluecircle.showarea()Circle.PI1# 可以通过类改变属性 PIprint(redcircle.PI)# 用的是类的属性 PIprint(bluecircle.PI)# 用的是类的属性 PIPython 如何实现让所有对象共享一个属性的呢很简单当 Python 发现类内存在属于类的属性时只需要在每个对象初始化时让对象中同名属性指向类属性即可。 因此我们试图通过某个对象修改属于类的属性则对其它对象不起作用因为修改的只是这个对象属性本身的指向所有的其它对象属性还在指向这个属于类的属性。classCircle():PI3.1415926def__init__(self,radius):self.radiusradius redcircleCircle(3)bluecircleCircle(5)redcircle.PI1# 只是改变了 redcircle 对象的属性 PI 指向print(redcircle.PI)# 1print(bluecircle.PI)# bluecircle 对象的属性 PI 还在指向 Cirle 类的属性 PIprint(Circle.PI)# Circle 类的属性 PI 只能通过 Cirle 类来改变访问控制我们在进行面向对象编程时最好不要直接通过对象来访问读或写属性而是通过类提供的函数进行读写属性操作要养成良好的编程习惯。classHuman():def__init__(self,myname):self.namemynamedefgetname(self):returnself.namedefsetname(self,newname):self.namenewname luren_aHuman(ruhua)print(luren_a.getname())# 访问属性luren_a.setname(wanghuahua)# 修改属性print(luren_a.getname())人的天性都是非自觉的程序员也是人所以需要约束我们刚刚讲过最好不要通过实例直接访问类的属性 那只是要求程序员自觉遵守所以我们可以强制性约束为了让内部属性不被外部访问 我们可以在属性的名称前加上两个下划线 __在 Python 中实例的变量名如果以 __ 开头就变成了一个私有变量private私有变量只有在类的内部可以访问读写在类的外部不能访问读写。classHuman():def__init__(self,myname):self.__namemynamedefgetname(self):returnself.__name# 类的内部可以访问defsetname(self,newname):self.__namenewname# 类的内部可以访问luren_aHuman(ruhua)print(luren_a.getname())luren_a.setname(wanghuahua)print(luren_a.getname())print(luren_a.__name)# 报异常因为类的外部无法访问luren_a.__namewanghuahua# 报异常因为类的外部无法访问有时候我们会遇到以单下划线开头的变量这样的实例变量在外部是可以访问的我们最好不要去访问这是一个不成文的约定。classHuman():def__init__(self,myname):self._namemyname luren_aHuman(ruhua)print(luren_a._name)# 类的外部可以访问 _name但最好不要访问要遵守约定Python 本身不进行访问控制的语法检查以双下划线开头的变量不能访问的原因 仅仅是因为 Python 在类内部把所有以__开头的变量进行了更名我们如果知道更改后的属性名就可以在外部进行访问该属性注意不同版本的 Python 解释器可能会把该变量改成不同的变量名。classHuman():def__init__(self,myname):self.__namemyname luren_aHuman(ruhua)print(luren_a._Human__name)# Python 3.7 版本把名字改成了 _Human__nameHuman.__namewanghuahuaprint(luren_a.__name)# Python 只对类内部 __开头的变量做改名所以可以访问# 我们可以通过 id 函数知道它们并不是一个变量print(id(luren_a.__name))print(id(luren_a._Human__name))3️⃣ 一切皆对象Python 的哲学是一切皆对象这是什么意思呢就是说 Python 中所有的东西都是对象比如 int 值float 值str 值bool 值list 值tuple 值dict 值set 值None 当然也包含我们自己定义类的对象。大家可能会说明明 int 值是 int 类型float 值是 float 类型str 值是 str 类型bool 值是 bool 类型list 值是 list 类型dict 值是 dict 类型set 值是 set 类型None 是 NoneType 类型自定义对象当然是自定义类的类型了。在此我告诉大家其实 Python 中的内置的这些数据类型本身就是用类实现的比如 int 是 class int 来实现的float 是 class float 来实现的等等。在此我再告诉大家一个秘密Python 中的内置数据类型和我们自定义的类本身也是个对象不要惊讶 甚至函数也是对象再说一遍不要惊讶。 我的意思是说Python 内置的所有类型intfloat listtupledictsetNoneType 和我们自定义的类以及函数都是对象。我们本节课学习两个函数 isinstance 和 type。isinstance 函数我们用 isinstance 函数可以判断一个东西的本质是什么下面我们就来验证在 Python 中一切皆对象。deffunc():passclassTest():passprint(isinstance(1,object))print(isinstance(1.1,object))print(isinstance(hello,object))print(isinstance(False,object))print(isinstance([1],object))print(isinstance((1,),object))print(isinstance({1:1},object))print(isinstance(set([1]),object))print(isinstance(None,object))print(isinstance(Test(),object))print(isinstance(int,object))print(isinstance(float,object))print(isinstance(str,object))print(isinstance(bool,object))print(isinstance(list,object))print(isinstance(tuple,object))print(isinstance(dict,object))print(isinstance(set,object))print(isinstance(type(None),object))print(isinstance(Test,object))print(isinstance(func,object))我们知道 isinstance 可以验证一个事物的本质是什么大家请思考一个有趣的问题假如我们自定义一个父类 Human 和子类 WomanWoman类继承 Human 类我们用 Woman 类定义一个对象 ruhua我们知道 ruhua 是 Woman 那么请问 ruhua 是不是也是 Humanruhua 当然也是人了。所以说子类的对象本质是定义它的类也是个它所属类的父类。classHuman(object):passclassWoman(Human):passruhuaWoman()print(isinstance(ruhua,Woman))# True如花是个女人print(isinstance(ruhua,Human))# True如花当然也是人type 函数我们知道 isinstance 函数可以判断一个东西的本质但是很多时候我们更想知道一个东西具体是属于什么类型 其实所有的对象int 值float 值str 值bool 值list 值tuple 值dict 值set 值None我们自己定义类的对象 都属于直接创建它们的类类型所有的 python 内置类intfloat listtupledictsetNoneType和自定义类继承 object 都属于 type 类型自定义类不继承 object属于 classobj 类型函数属于 function 类型。 Type 函数可以判断一个东西具体所属的类型我们通过下面表格来了解。被判断的东西解释例子int 值int 值属于 int 类型type(1) 返回 intfloat 值float 值属于 float 类型type(1.1) 返回 floatstr 值str 值属于 str 类型type(“hello”) 返回 strbool 值bool 值属于 bool 类型type(False) 返回 boollist 值list 值属于 list 类型type([1]) 返回 listtuple 值tuple 值属于 tuple 类型type((1,)) 返回 tupledict 值dict 值属于 dict 类型type([1]) 返回 dictset 值set 值属于 set 类型type(set([1])) 返回 set自定义类的对象对象属于自定义类类型type(对象) 返回自定义类名intint 属于 type 类型type(int) 返回 typefloatfloat 属于 type 类型type(float) 返回 typeboolbool 属于 type 类型type(bool) 返回 typelistlist 属于 type 类型type(list) 返回 typetupletuple 属于 type 类型type(tuple) 返回 typedictdict 属于 type 类型type(dict) 返回 typesetset 属于 type 类型type(set) 返回 type自定义类类属于 type 类型type(类) 返回 type函数函数 属于 function 类型type(函数) 返回 function注意自定义的类继承 object 和不继承 object 的 type 有差别为了更好的理解上面的表格大家运行一下下面的代码。deffunc():passclassTest():passclassMyobject(object):passprint(type(1))print(type(1.1))print(type(hello))print(type(False))print(type([1]))print(type((1,)))print(type({1:1}))print(type(set([1])))print(type(None))print(type(Myobject()))print(type(int))print(type(float))print(type(str))print(type(bool))print(type(list))print(type(tuple))print(type(dict))print(type(set))print(type(type(None)))print(type(Myobject))print(type(func))4️⃣ 类的职责要单一定义类之前先写清它代表什么对象、保存哪些状态、提供哪些操作。实例方法用于处理具体对象类方法用于处理类级别的数据或创建替代构造方式静态方法适合与类概念相关、但不依赖实例和类状态的辅助逻辑。类过大时应优先拆分职责而不是继续增加方法。 文章小结类描述对象的共同结构对象保存各自的状态类属性和实例属性的查找与赋值方式不同。__init__初始化实例从类调用方法和从实例调用方法时要理解实例参数如何绑定。访问控制主要依赖命名约定。isinstance()可考虑继承关系type()返回对象的实际类型。可设计职工类再把年薪统计、排序等集合操作放在管理职工的代码中。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门