Python面向对象:封装和多态

1、封装

封装是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。python

封装就是将抽象获得的数据和行为(或功能)相结合,造成一个有机的总体,也就是将数据与操做数据的源代码进行有机的结合,造成“类”,其中数据和函数都是类的成员。程序员

一、简单理解封装

顾名思义,封装属性就是把已有的属性封装到一个类里面去:安全

class Person(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex jack = Person('jack', 18, '男') #将jack、 1八、 男 封装到jack对象(self)的name、age、sex中 #name、age、sex又封装在了Person类中 print(jack.__dict__) #{'name': 'jack', 'age': 18, 'sex': '男'} 

分析:self是一个形式参数,建立什么对象,它就表明那个对象函数

二、调用封装的属性

经过对象直接调用:测试

class Person(): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex jack = Person('jack', 18, '男') print(jack.name, jack.sex, jack.age) #jack 男 18 

分析:在这里,咱们能够用对象名随意调用到自身的属性,并进行属性修改,从安全的角度来看,这样是很不安全的,因此须要将属性隐藏起来,也就是私有化。ui

私有化属性的方法:类中定义私有的,只有类内部使用,外部没法访问(好比_(杠) __(杠杠) )this

class Person(): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex jack = Person('jack', 18, '男') print(jack._Person__name)#jack print(jack.name) #Error:'Person' object has no attribute 'name' 

分析:
一、经过使用__(杠杠)的方法使得类Person属性name、age、sex成功私有化,子类没法直接调用,可是经过jack._Person__name的方式能够调用到私有化的属性,而且能对其修改,说明python在设置私有属性的时候,只是把属性的名字换成了其余的名字。spa

二、类中以_或者__的属性,都是私有属性,禁止外部调用。虽然能够经过特殊的手段获取到,而且赋值,可是这么作不觉的很蛋疼么,原本就是设置私有属性,还非要去强制修改。code

私有化属性设置好了,不多是存在那里谁都不让使用的,要否则设置私有化属性就失去了自己的意义,咱们只是不想让私有化属性直接被随意的修改,而不是拒绝访问,因此还须要给私有化属性提供查找和修改的接口,咱们只须要经过对接口的控制,就能有效的控制私有化属性的数据安全,好比对接口进行设置,就不会出现age被赋值为负数。对象

class Person(object): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex def get_age(self): return self.__age def set_age(self, age): if age > 150 or age < 0: print('年龄必须大于0,小于150') else: self.__age = age jack = Person('jack', 18, '男') #访问age属性 print(jack.get_age())#18 #修改age属性 jack.set_age(100) print(jack.get_age())#100 #非法修改age属性 jack.set_age(-20)#年龄必须大于0,小于150 print(jack.get_age())#100 

分析:这样就完美了,咱们既能够访问到实例化对象内部的属性,也能够在数据安全的状况下(禁止非法数据修改),修改对象的属性

三、python自带的调用私有化数据的方法

前面,咱们用set和get的方式来调用或修改对象自己的私有化属性,达到了数据安全的目的,其实python中提供了一种直接用obj.属性名的方式调用类的私有化属性,也能保证数据安全。

class Person(object): def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex @property def age(self): return self.__age @age.setter def age(self, age): if age > 150 or age < 0: print('年龄必须大于0,小于150') else: self.__age = age jack = Person('jack', 18, '男') #访问age属性 print(jack.age)#18 #修改age属性 jack.age = 100 print(jack.age)#100 #非法修改age属性 jack.age = -20#年龄必须大于0,小于150 print(jack.age)#100 

分析:
一、使用 @property 装饰器时,接口名没必要与属性名相同。

二、凡是赋值语句,就会触发set方法。获取属性值,会触发get方法。

三、咱们可使用@property装饰器来建立只读属性,@property装饰器会将方法转换为相同名称的只读属性,能够与所定义的属性配合使用,这样能够防止属性被修改。

2、多态

接口的多种不一样的实现方式即为多态。

多态最核心的思想就是,父类的引用能够指向子类的对象,或者接口类型的引用能够指向实现该接口的类的实例。

多态是一种运行期的行为,不是编译期行为!在编译期间它只知道是一个引用,只有到了执行期,引用才知道指向的是谁。这就是所谓的“软绑定”。

多态是一项让程序员“将改变的事物和未改变的事物分离开来”重要技术。

一、多态性

多态性是指指具备不一样功能的函数可使用相同的函数名,这样就能够用一个函数名调用不一样内容的函数。

在面向对象方法中通常是这样表述多态性:向不一样的对象发送同一条消息,不一样的对象在接收时会产生不一样的行为。

不一样的行为就是指不一样的实现,即执行不一样的函数。

class Animals(object): def talk(self): pass class Person(Animals): def talk(self): print('高级语言') class Cat(Animals): def talk(self): print('喵喵喵') class Dog(Animals): def talk(self): print('汪汪汪') per = Person() cat = Cat() dog = Dog() # 定义一个统一的接口来访问 def fun(obj): obj.talk() fun(per)#高级语言 fun(cat)#喵喵喵 fun(dog)#汪汪汪 

分析:
一、per对象、cat对象、dog对象是经过Animals类实现的三种不一样形态,这就是多态的体现。

二、per、cat、dog对象都是经过fun(obj)的同一种方式调用,实现了不一样的效果,这就是多态性的体现,因此多态性能够说是一个接口,多种实现

三、多态性的优势:
3.一、增长了程序的灵活性:以不变应万变,不论对象变幻无穷,使用者都是同一种形式去调用,如fun(obj)
3.二、增长了程序额可扩展性:经过继承Animal类派生新的类(Person类、Cat类、Dog类),使用者无需更改本身的代码,仍是用fun(obj)去调用

二、鸭子类型

调用不一样的子类将会产生不一样的行为,而无须明确知道这个子类其实是什么,这是多态的重要应用场景。而在python中,由于鸭子类型(duck typing)使得其多态不是那么酷,缘由是python是强类型的动态脚本语言,不使用显示数据类型声明,且肯定一个变量的类型是在第一次给它赋值的时候。

鸭子类型是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”能够这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”

在鸭子类型中,关注的不是对象的类型自己,而是它是如何使用的。例如,在不使用鸭子类型的语言中,咱们能够编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数能够接受一个任意类型的对象,并调用它的"走"和"叫"方法。若是这些须要被调用的方法不存在,那么将引起一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象均可被函数接受的这种行为引出了以上表述,这种决定类型的方式所以得名。

鸭子类型一般得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

class Duck(object): def walk(self): print('I walk like a duck') def swim(self): print('I swim like a duck') class Person(): def walk(self): print('this one walk like a duck') def swim(self): print('this man swim like a duck') def fun(obj): obj.walk() obj.swim() fun(Duck()) # I walk like a duck # I swim like a duck fun(Person()) #this one walk like a duck #this man swim like a duck 

分析:能够看出Pseron类拥有和Duck类同样的方法,当程序调用Duck类,并利用了其中的walk和swim方法时,咱们传入Person类也同样能够运行,程序并不会检查类型是否是Duck,只要他拥有 walk()和swim()方法,就能被正确地调用。

再举例,若是一个对象实现了__getitem__方法,那python的解释器就会把它当作一个collection,就能够在这个对象上使用切片,获取子项等方法;

若是一个对象实现了__iter__和next方法,python就会认为它是一个iterator,就能够在这个对象上经过循环来获取各个子项。

class Foo: def __iter__(self): pass def __next__(self): pass from collections import Iterable from collections import Iterator print(isinstance(Foo(), Iterable)) # True print(isinstance(Foo(), Iterator)) # True 

所以,这就诠释了“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。”这句话,那只鸟是否是鸭子不重要,重要的是它有和鸭子同样的方法,把它当鸭子调用,程序就不会报错。

交流基地:630390733

相关文章
相关标签/搜索