文章目录
一、面向对象三大特性
-
封装
- 将属性和方法书写到类的里面的操作即为封装
- 封装可以为属性和方法添加私有权限
- 确保对象中数据的安全
-
继承
- 子类默认继承父类的所有属性和方法
- 子类可以重写父类属性和方法
- 保证了对象的扩展性
-
多态
- 传入不同的对象,产生不同的结果
- 保证了程序的灵活性
二、类的封装
python的面向对象, 并没有严格意义上的私有属性和方法, 私有只是一种约定, 隐藏实现的细节,只对外公开我们想让他们使用的属性和方法,这就叫做封装,封装的目的在于保护类内部数据结构的完整性, 因为使用类的用户无法直接看到类中的数据结构,只能使用类允许公开的数据,很好地避免了外部对内部数据的影响,提高了程序的可维护性。用户只能通过暴露出来的方法访问数据时,你可以在这些方法中加入一些控制逻辑,以实现一些特殊的控制
2.1 定义私有属性和方法
在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()
注意:私有属性和私有方法只能在类里面访问和修改。
2.2 获取和修改私有属性值
在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()) # 钢铁侠
三. 多态
3.1 了解多态
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
- 定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
- 好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
- 实现步骤:
- 定义父类,并提供公共方法
- 定义子类,并重写父类方法
- 传递子类对象给调用者,可以看到不同子类执行效果不同
3.2 体验多态
- 需求:警务人员和警犬一起工作,警犬分为2种:追击敌人和追查毒品,携带不同的警犬,执行不同的工作。
# 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) # 追查毒品...
四、类属性和实例属性
4.1 类属性
4.1.1 设置和访问类属性
- 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。
- 类属性可以使用 类对象 或 实例对象 访问。
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 中国
类属性的优点
- 记录的某项数据 始终保持一致时,则定义类属性。
- 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有 ,仅占用一份内存,更加节省内存空间。
4.1.2 修改类属性
类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了一个实例属性。
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) # 北京
4.2 实例属性
类是一种封装技术,我们把数据和方法都封装到了类里,如果你想使用数据和方法,那么你需要用类创建出一个实例来,这个过程就叫做实例化
实例属性 通过实例对象添加的属性是实例属性
实例属性只能通过实例对象来访问和修改,类对象无法访问和修改
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() 这行代码是在调用实例的。
4.2.1 实例方法
实例方法是在我们类中直接定义的,以self为第一个参数开头的都是实例方法
当通过实例对象去调用时,会自动将当前对象作为self传入
当通过类调用时,不会自动传递self
class Stu(object):
def test(self):
print('我是test方法...')
五、类方法和静态方法
5.1 类方法
5.1.1 类方法特点
- 需要用装饰器
@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数。
5.1.2 类方法使用场景
- 当方法中 需要使用类对象 (如访问私有类属性等)时,定义类方法
- 类方法一般和类属性配合使用
- 类方法可以通过类或类的实例访问
class Dog(object):
__tooth = 10
@classmethod
def get_tooth(cls):
return cls.__tooth
wangcai = Dog()
result = wangcai.get_tooth()
print(result) # 10
5.2 静态方法
5.2.1 静态方法特点
- 需要通过装饰器
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。 - 静态方法 也能够通过 实例对象 和 类对象 去访问。
5.2.2 静态方法使用场景
- 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
- 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
class Dog(object):
@staticmethod
def info_print():
print('这是一个狗类,用于创建狗实例....')
wangcai = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
wangcai.info_print()
Dog.info_print()