上一篇:Python 类与对象编程详解三(类属性、实例属性)
封装不是单纯意义的隐藏
加粗样式
1、封装数据:主要原因是:保护私隐,明确区分内外。将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
实例1:
class teacher:
def __init__(self,name,age):
self.__name = name
self.__age = age
def tell_info(self):
print("姓名:%s,年龄:%s"%(self.__name,self.__age))
def set_info(self,name,age):
if not isinstance(name,str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age,int):
raise TypeError('年龄必须是整形')
self.__name = name
self.__age = age
t = teacher('jack',19)
t.tell_info()
t.set_info('jery',18)
t.tell_info()
运行结果:
姓名:jack,年龄:19
姓名:jery,年龄:18
目的是隔离复杂度
实例:
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做隔离了复杂度,同时也提升了安全性
class ATM:
def __card(self):
print('插卡')
def __auth(self):
print('用户认证')
def __input(self):
print('输入取款金额')
def __print_bill(self):
print('打印账单')
def __take_money(self):
print('取款')
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
a=ATM()
a.withdraw()
运行结果:
插卡
用户认证
输入取款金额
打印账单
取款
比如说私有变量和私有方法,如果你还不太了解,请看这篇文章
Python 类与对象编程详解二(成员保护和访问限制)
1.类中定义的__x只能在内部使用,如self.__x,引用的就是私有属性的结果。外部是无法通过__x这个名字访问到的。
2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
什么是特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
在上一篇:Python 类与对象编程详解三(类属性、实例属性)
中也有property的实例,这里不在举例
public
这种其实就是不封装,是对外公开的
protect
下面的代码会用到装饰器,如果您还不太了解,请看这篇文章
Python 装饰器
实例:
class people(object):
def __init__(self):
self._x = 'male' # # p2.sex=male 一初始化或赋值操作就找 sex.setter
@property # 负责查询
def x(self): # 通过接口可以查看隐藏的性别
return self._x # p2.__sex= male
@x.setter# 定义修改性别的接口
def x(self,value):
# sexes=['male','female']
if not isinstance(value,str): # 在设定值之前进行类型检查,增加限制的扩展性
raise TypeError('性别必须是字符串类型')
# if value not in sexes:
# raise TypeError('性别只能是 male 或者 female')
self._x=value # p2.__sex= male
@x.deleter
def x(self): # 删除属性接口
del self._x # del p2.__sex
p2=people() # 触发 init 执行,这里有个赋值操作 p2.sex='male'
print(p2.x)
p2.x = 'female'
print(p2.x)
del p2.x # 删掉 __sex 数据属性
print(p2.x) # 再去 property 找的话找不到了
运行结果:
male
female
AttributeError: 'people' object has no attribute '_x'
private
这种封装对谁都不公开
class people(object):
def __init__(self):
self.__name = "hello"
Jack = people()
print(Jack.__name)
print(people.__name)
上面的代码执行就会报错,因为类和对象不能再类外访问私有属性
继承是一种创建新的类的方式,新创建的叫子类,继承的叫父类、超类、基类。
特点:子类可以使用父类的属性,方法
继承是类与类之间的关系
减少代码冗余、提高重用性
运行结果
class animal():
print("我是动物")
class pet(animal):
print("我是宠物")
class cat_pet(pet):
print("我是宠物猫")
cat = cat_pet
运行结果:
我是动物
我是宠物
我是宠物猫
class animal():
print("我是动物")
class pet():
print("我是宠物")
class cat_pet(pet,animal):
print("我是宠物猫")
cat = cat_pet
运行结果:
我是动物
我是宠物
我是宠物猫
使用魔法方法__bases__方法可以获取子类继承的类
如果你还想了解其他的魔法方法,请看这篇文章
Python 高级用法
实例:
class animal():
print("我是动物")
class pet():
print("我是宠物")
class cat_pet(pet,animal):
print("我是宠物猫")
print(cat_pet.__bases__)
运行结果:
我是动物
我是宠物
我是宠物猫
(<class '__main__.pet'>, <class '__main__.animal'>)
在Python3中如果一个类没有继承任何类,则默认继承object类。因此python3中都是新式类
抽象:通过抽象可以得到类,抽象是一种分析的过程。可以从猪、猫、狗等中,可以抽象出一个动物类。先分析、抽象之后,就可以通过继承,在程序上实现这个结构。
class Animals:
pass
class Pig(Animals):
pass
class Dog(Animals):
pass
class Cat(Animals):
pass
概念:派生,就是在子类继承父类的属性的基础上,派生出自己的属性。子类有不同于父类的属性,这个子类叫做派生类。通常情况下,子类和派生类是同一个概念,因为子类都是有不同于父类的属性,如果子类和父类属性相同,就没必要创建子类了。
实例1:
class Animals:
pass
class Dog(Animals):
pass
这时候Dog类不是派生类
实例2:
class Animals:
def __init__(self, name):
self.name = name
def walk(self):
print('我会走')
class Dog(Animals):
#Dog类派生出bite功能
#派生:狗有咬人的技能
def bite(self):
print('我会咬人')
这个类就是派生类
除了继承之外,还有一种提高重用性的方式:组合
组合指的是,在一个类A中,使用另一个类B的对象作为类A的数据属性(特征)(变量),称为类的组合
比如:人和手机,人想要有打电话的功能,想要打电话,就需要用到手机,人想要用到手机里面的打电话功能,肯定不能用继承,人继承手机就非常尴尬了,这时候就可以用到组合。
class Mobile():
def __init__(self, name):
self.name = name
def call(self):
print('我要打电话')
class People():
def __init__(self, name, mobile):
self.name = name
self.mobile = mobile
mobile = Mobile('华为')
people = People('小白', mobile)
people.mobile.call()
运行结果:
我要打电话
继承建立了派生类和基类的关系,是一种是的关系,比如白马是马,人是动物。
组合建立了两个类之间’有’的关系,比如人有手机,然后人可以使用手机打电话。
对象查找属性的顺序:对象自己的 - > 所在类中 -> 找父类 - >父类的父类 ->Object
当子类出现了与父类名称完全一致的属性或是方法,子类就会重写父类的方法
实例:
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def speak(self):
print("Hello")
class student(Person):
def speak(self):
print("world")
Jack = student('jack',19)
Jack.speak()
运行结果:
world
方式1:
super(当前类名称,self).你要调的父类的属性或方法
方式2:
super().你要调的父类的属性或方法
方式3:
类名称.你要调的父类的属性或方法(self)
实例:
class Person(object):
name = "jack"
def __init__(self,age):
self.age = age
def speak(self):
print("Hello")
class student(Person):
def speak(self):
print(super(student,self).name)
Jack = student(19)
Jack.speak()
运行结果:
jack
当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数
实例:
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def speak(self):
print("Hello")
class student(Person):
def __init__(self,name,age,grade):
super(student,self).__init__(name,age)
self.grade = grade
def speak(self):
print("world")
Jack = student('jack',19,2)
Jack.speak()
运行结果:
world
mro方法可以列出类继承的所有父类,并且有顺序。调用mro方法要用类名.mro
实例:
class Person(object):
name = "jack"
def __init__(self,age):
self.age = age
def speak(self):
print("Hello")
class student(Person):
def speak(self):
print(super(student,self).name)
Jack = student(19)
print(student.mro())
运行结果:
[<class '__main__.student'>, <class '__main__.Person'>, <class 'object'>]
类的继承图是从一个类开始继承,继承到顶点时,多个类继承同一个顶点,这种就叫做菱形继承。菱形继承查找类时就用广度查找。
遇到共同父类时就广度
经典类都是深度优先
实例:
class A(object):
print("我是A")
class B(A):
print("我是B")
class C(A):
print("我是C")
class D(B,C):
print("我是D")
d = D()
运行结果:
我是A
我是B
我是C
我是D
Python中多态是指一类事物有多种形态。比如动物有多种形态,人,狗,猫,等等。文件有多种形态:文本文件,可执行文件。
①增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
②增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
实例:
import abc
class Animals(metaclass=abc.ABCMeta):
@abc.abstractmethod
def talk(self):
pass
class People(Animals):
def talk(self):
print('People is talking')
class Cat(Animals):
def talk(self):
print('Cat is miaomiao')
class Dog(Animals):
def talk(self):
print('Dog is wangwang')
cat1 = Cat()
dog1 = Dog()
peo1 = People()
# peo、dog、pig都是动物,只要是动物肯定有talk方法
# 于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo1.talk()
dog1.talk()
peo1.talk()
# 定义一个统一的接口来访问
def func(obj):
obj.talk()
func(cat1)
运行结果:
People is talking
Dog is wangwang
People is talking
Cat is miaomiao
要理解什么是多态,首先要对数据类型再做一点说明、在定义一个class的时候,实际上就定义了一种数据类型,通常,定义的数据类型和python自带的数据类型(例如string、list、dict)没什么区别
a = list() a 是list类型
b = animal() b 是animal 类型
c = Dog() c 是 Dog 类型
一个变量是否为某个类型可以用isinstance()判断
实例:
class Animal:
def run(self):
print("Animal is runing...")
class Cat(Animal):
def run(self):
print("Cat is runing...")
class Dog(Animal):
def run(self):
print("Dog is runing...")
c = Dog()
c.run()
print(isinstance(c,Animal))
print(isinstance(c,Dog))
运行结果:
Dog is runing...
True
True
因为Dog是从Animal继承的,当创建了一个Dog的实例c时,认为c的数据类型是Dog没错,但c同时也是Animal,Dog本来也是Animal的一种
所以,在继承关系中,如果一个实例的数据类型是某个子类,那么它的数据类型也可以看做事是父类,但是,反过来就不可以
>>> b = Animal()
>>> isinstance(b,Dog)
False
Dog可以看成Animal,但Animal不可以看作Dog
要理解多态的好处,还需要编写一个函数,这个函数接收一个Amimal类型的变量
def run_twice(animal):
animal.run()
animal.run()
当传入Animal的实例时,run_twice就打印出:
>>> run_twice(Animal())
Animal is runing...
Animal is runing...
当传入Dog的实例时,run_twice()就打印出
>>> run_twice(Dog())
Dog is runing...
Dog is runing...
当传入Cat的实例时,run_twice()就打印出
>>> run_twice(Cat())
Catis runing...
Catis runing...
现在,如果定义一个Tortoise类型,也从Animal派生
class Tortoise(Animal):
def run(self):
print('Tortoise is running slowly')
当调用run_twice()时传入Tortoise实例
>>> run_twice(Tortoise())
Tortoise is running slowly
Tortoise is running slowly
大家会发现新增了一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
多态地好处就是,当传入Dog,Cat,Tortoise等时,只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise等都是Animal类型,然后按照Animal类型进行操作。由于Animal类型有run()方法,因此传入的任意类型,只要是Animal或者Animal的子类,就会自动调用实际类型的run()方法,这就是多态的意思。
对于一个变量,用户只需要知道它是Animal类型,无须确切的知道它的子类型,就可以放心的调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat、Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力,调用方法只管调用,不管细节,而当新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的,这就是著名的"开闭"原则,包括以下两点。
1、对扩展开放:允许新增Animal子类
2、对修改封闭:不需要修改依赖Animal类型的run_twice()等函数
下一篇:Python 类与对象编程详解五(特殊成员)