1. 特殊方法
- 1.1 引入特殊方法的原因:
- 在进行对象实例化的过程中,对象的调用者往往很容易遗漏一些操作,导致调用者在使用对象方法时出现程序错误。针对这个问题,Python在实例化对象后,专门对对象内部数据做一些初始化操作,这些操作的打包就叫特殊方法,由对象自动调用,无序人工干预。
- 1.2 双下划线开头 双下划线结尾
- 例如:__ init __(self,…) … 传入参数
- 示例:
class Person(): # init 方法用来初始化某个对象的属性 def __init__(self,name): print('我正在执行init方法....') self.name = name def p(self): print(f"Hello, 我是你{self.name}") p1 = Person('爸爸') p1.p() p2 = Person('妈妈') p2.p()
- 运行结果:
- 1.3 特殊方法不需要我们自己调用,特定的时候自己调用
- 例如:__ init __ 方法是在初始化实例对象后立即调用 ,请看上图中的提示"我正在执行init方法",代码并没有手动调用init方法。
2. 封装
- 2.1 出现封装的原因是我们就需要一种方法来增强数据的安全性
- 属性不能随意修改(这里指隐藏属性名字,同时为修改和访问属性提供接口方法)
- 属性不能改为任意的值(这里指修改属性时要对值条件检查)
- 示例:
class Car(): def __init__(self,name,color,speed): self.name = name self.color = color self.speed = speed def inst(self): print(f'{self.name}汽车是{self.color}...') def run(self): print(f'{self.name}的速度是{self.speed} km/s') c = Car('奥迪','黑色',3000) c.inst() c.run() # name 虽然被封装了,但仍可通过 对象.属性名 的方式修改。 # 封装并不是真的把属性隐藏所有人都看不到,这只是一种对象的安全访问机制 c.name = '宝马' c.inst() c.run()
- 运行结果:
- 2.2
封装:
隐藏对象中一些不希望被外部访问到的属性或方法,它是面向对象的三大特性之一。- getter() 可以访问对象属性
- setter() 可以修改对象属性
class Boy: def __init__(self, name, age, love): self.hiddem_name = name self.hiddem_age = age self.hiddem_love = love def description(self): print(f'我叫{self.hiddem_name},今年{self.hiddem_age}岁,爱好{self.hiddem_love}') def get_love(self): return self.hiddem_love def set_love(self,love): if isinstance(love,str): # 必须是字符串类型 self.hiddem_love = love def get_age(self): return self.hiddem_age def set_age(self,age): if 0 < age <= 100: self.hiddem_age = age d = Boy("小明",5,'足球') d.description() d.set_age(10) # 通过方法修改爱好 d.set_love('王者荣耀') d.description() # 参数检测 :love = 10 ,非字符串 d.set_love(10) d.description() # 这种方式并没有修改对象中的hiddem_name d.name = '小强' d.description() # 直接修改对象内部变量,成功修改。可知通过改变对象内部的属性名也可提高数据安全 # 但这种方式不彻底,请看下例中更为高级的用法 d.hiddem_name = '小李' d.description() # 获得小李的年龄 print(d.get_age()) d.set_love('吃吃吃') # 获得小李的爱好 print(d.get_love())
- 运行结果:
- 小结:封装在保护数据安全的同时,我们虽然不知对象的内部属性细节,但python友好的允许我们使用对象提供的一些方法去操作控制对象,如下图所示:
- 2.3 封装的优势:使用后,确实增加了类的定义的复杂程度,但是也确保了数据的安全性
- 隐藏属性名,调用者就无法看到对象内部定义的属性名了
- 增加了getter()和setter()方法,很好控制属性是否只读
- 使用setter()设置属性,可以增加判断传入的参数是否合法
- 使用getter()获取属性,使用setter()设置属性可以在读取属性和修改属性的同时做一些其他的处理
- 2.4 使用双下划线开头来对对象属性进行封装,双下划线开头的属性,是对象的隐藏属性。它只能在类内访问,不可通过
对象.属性名
直接访问,但可使用_类名__ 属性名
的方式访问,那么这种封装的方式其实就是给属性起了另外的一个名字。例如 __name --> _ Persom __ name
。- 示例:
class Person(): def __init__(self, name): self.__name = name def get_name(self): return self.__name def set_name(self): self.__name = name p = Person('马云') print(p.get_name()) # 这里对比上例中的hiddem_name 更安全了 # 双下划线开头,是对象的隐藏属性,该属性只能在类内访问,无法提供对象.属性名访问 p.__name = '淘宝' print(p.get_name()) # 其隐藏属性只不过将隐藏属性自动改了名字 _类名__属性名,但通过后者还是可以访问对象属性 p._Person__name = '淘宝' print(p.get_name())
- 运行结果:
- 虽然这种方式可以在外部访问,但从封装的角度考虑,我们一般不这样用。
- 为了书写简便和访问安全,我们一般把 双下划线 改为 单下划线,这样通过
_类名__ 属性名
的方式也无法访问对象内部属性。这样书写简单同时,又能保证了数据的安全性,还可以将单下划线开头的属性习惯规定为私有属性,真可谓是一举三得,哈哈。 - 示例:
# 该类的内部的所有属性名将双下划线换成单下划线 class Person(): def __init__(self, name): self._name = name # 习惯将内部属性名使用单下划线开头 def get_name(self): return self._name def set_name(self,name): self._name = name p = Person('马云') print(p.get_name()) # 属性名采用单下划线,使得通过双下划线的方式无法修改类内部属性 p.__name = '淘宝' print(p.get_name()) # 属性名采用单下划线,使得通过 ‘_类__属性名’ 的方式无法修改类内部属性 p._Person__name = '淘宝' print(p.get_name()) # 以上两种正规的python访问方式都没有将类内属性修改成功,变相的提高了数据的安全性 # 使用单下划线比双下划线书写简单,虽然通过单下划线方式也可以修改属性,造成数据的不安全 # 但从数据安全和编程速度的方面综合考虑,选择将单下划线开头定义属性名比以上例子中的方式都好。 p._name = '淘宝' print(p.get_name())
- 运行结果:
- 示例:
3. property 装饰器
我们可以使用@property 装饰器来创建只读属性,@property 装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
这句话不理解的可以看这儿- 使用装饰器后如下例中将 属性名,setter()和getter() 方法名字统一变成属性名。
- 使用
对象.属性名
一种方式就可完成 setter() 和getter()两种方法,使得面向对象编程更加方便、简单。 对象.属性名
后不再需要括号或括号传参(使用装饰器后加括号()
会报错),更加符合初学者的使用习惯,入门比较简单。- 示例:
class Person(): def __init__(self, name): self._name = name @property def name(self): return self._name # setter 方法的装饰器 @ 属性名.setter @name.setter def name(self,name): self._name = name p = Person('马云') print(p.name) p.name = '马化腾' print(p.name)
- 运行结果:
4. 总结
- 通过今天面向对象的学习,我特别想说一些自己以前的经历和感悟。我以前学过C 、C++编程。当学完C的时候,懂得了面向过程的编程方式,它符合人类的思维方式,编写代码容易上手,但代码复用性底,不适合大型程序的开发。当我学C++时,学会了面向对象的编程方式,懂得用更抽象方式类、封装、继承、多态的思想去编写大型的程序,它可以使程序结构清晰,修改维护更加方便,复用性高。当我再用面向对象思想去编写C语言稍大程序时,发现简单容易多了(但遗憾的C没有C++的类、封装、继承等等,即使这样的编程好过面向过程的编程方式),今天呢,我又学到了Python中面向对象的封装技术,发现它比C++的封装技术用的更好(包括特殊方法、隐藏属性、装饰器等),使Python的数据安全性更高和用户编程上更加方便和高效。我想在我以后C++编程上也同样可以用到Python这些高级的编程技巧和思想,任何语言的编程思想都时相通的,我坚信只有这些才是我们真正需要学习并借鉴的,我也为选择学习Python这门语言感到非常高兴!!!