封装
继承
多态
python的面向对象, 并没有严格意义上的私有属性和方法, 私有只是一种约定, 隐藏实现的细节,只对外公开我们想让他们使用的属性和方法,这就叫做封装,封装的目的在于保护类内部数据结构的完整性, 因为使用类的用户无法直接看到类中的数据结构,只能使用类允许公开的数据,很好地避免了外部对内部数据的影响,提高了程序的可维护性。用户只能通过暴露出来的方法访问数据时,你可以在这些方法中加入一些控制逻辑,以实现一些特殊的控制
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
故事:daqiu把技术传承给徒弟的同时,不想把自己的钱(2000000个亿)继承给徒弟,这个时候就要为钱这个实例属性设置私有权限。
设置私有权限的方法:在属性名和方法名 前面 加上两个下划线 __。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
# 定义私有属性
self.__money = 2000000
# 定义私有方法
def __info_print(self):
print(self.kongfu)
print(self.__money)
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 徒孙类
class Tusun(Prentice):
pass
daqiu = Prentice()
# 对象不能访问私有属性和私有方法
# print(daqiu.__money)
# daqiu.__info_print()
xiaoqiu = Tusun()
# 子类无法继承父类的私有属性和私有方法
# print(xiaoqiu.__money) # 无法访问实例属性__money
# xiaoqiu.__info_print()
注意:私有属性和私有方法只能在类里面访问和修改。
在Python中,使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性,,一般定义函数名get_xx用来获取私有属性,定义set_xx用来修改私有属性值。
如果希望属性是只读的,则可以直接去掉setter方法
如果希望属性不能被外部访问,则可以直接去掉getter方法
使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
使用getter方法获取属性,使用setter方法设置属性可以在读取属性和修改属性的同时做一些其他的处理
使用getter方法可以表示一些计算的属性
getter获取对象中的指定属性( get_ 属性名)
setter用来设置对象的指定属性( set_属性名)
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
self.__money = 2000000
# 获取私有属性
def get_money(self):
return self.__money
# 修改私有属性
def set_money(self):
self.__money = 500
def __info_print(self):
print(self.kongfu)
print(self.__money)
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 徒孙类
class Tusun(Prentice):
pass
daqiu = Prentice()
xiaoqiu = Tusun()
# 调用get_money函数获取私有属性money的值
print(xiaoqiu.get_money())
# 调用set_money函数修改私有属性money的值
xiaoqiu.set_money()
print(xiaoqiu.get_money())
在封装过程中可以对对象的属性使用双下划线的方式__xxx ,其实这种封装的方式只不过是Python自动的给属性起了一个名字,这个名字的样式是 _类名__属性名 例如 __name --> _Person__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._Person__name = '钢铁侠'
print(p.get_name()) # 钢铁侠
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
# 1. 定义父类:提供公共方法:警犬和人
class Dog(object):
def work(self): # 父类提供统一的方法,哪怕是空方法
print('指哪打哪...')
# 2. 定义子类:子类重写父类方法:定义2个类表示不同的警犬
class ArmyDog(Dog): # 继承Dog类
def work(self): # 子类重写父类同名方法
print('追击敌人...')
class DrugDog(Dog):
def work(self):
print('追查毒品...')
# 定义人类
class Person(object):
def work_with_dog(self, dog): # 传入不同的对象,执行不同的代码,即不同的work函数
dog.work() # 形参调用方法
# 3. 创建对象,调用不同的功能,传入不同的对象,观察执行的结果
ad = ArmyDog()
dd = DrugDog()
daqiu = Person()
daqiu.work_with_dog(ad) # 追击敌人...
daqiu.work_with_dog(dd) # 追查毒品...
class People(object):
country = '中国'
print(id(People.country), People.country) # 1386329265232 中国 # 通过类去访问
p = People()
# 实例对象p并没有country属性,但会从类属性里找到同名的属性
print(id(p.country), p.country) # 1386329265232 中国
p.country = "美国" # 创建了country实例属性, 而不是修改了类的country属性
print(id(p.country), p.country) # 1386329265424 美国
# print(id(People.country), People.country) #1386329265232 中国
类属性的优点
- 记录的某项数据 始终保持一致时,则定义类属性。
- 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有 ,仅占用一份内存,更加节省内存空间。
类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了一个实例属性。
class People(object):
country = '中国'
p = People()
l = People()
# 修改类属性
People.country = '北京'
print(People.country) # 北京
print(p.country) # 北京
print(l.country) # 北京
# 不能通过对象修改属性,如果这样操作,实则是创建了一个实例属性
p.country = '上海'
print(People.country) # 北京
print(p.country) # 上海
print(l.country) # 北京
类是一种封装技术,我们把数据和方法都封装到了类里,如果你想使用数据和方法,那么你需要用类创建出一个实例来,这个过程就叫做实例化
实例属性 通过实例对象添加的属性是实例属性
实例属性只能通过实例对象来访问和修改,类对象无法访问和修改
class Stu(object):
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
print("{name} is running".format(name=self.name))
s = Stu('小明', 18)
print(s.age) # 18
# print(Stu.age) # 报错:实例属性不能通过类访问
s.run() # 小明 is running
s 就是创建出来的实例,s是类Stu的一个实例。在定义Stu的时候,要求实例必须有name和age属性,因此,在创建示例时,我们必须提供这些属性,按照__init__函数的参数列表传入’小明’ 和 18。
s.run() 这行代码是在调用实例的。
实例方法是在我们类中直接定义的,以self为第一个参数开头的都是实例方法
当通过实例对象去调用时,会自动将当前对象作为self传入
当通过类调用时,不会自动传递self
class Stu(object):
def test(self):
print('我是test方法...')
@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数。class Dog(object):
__tooth = 10
@classmethod
def get_tooth(cls):
return cls.__tooth
wangcai = Dog()
result = wangcai.get_tooth()
print(result) # 10
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。class Dog(object):
@staticmethod
def info_print():
print('这是一个狗类,用于创建狗实例....')
wangcai = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
wangcai.info_print()
Dog.info_print()