学习python的第十四节课:面对对象 -- 类的封装、多态与属性方法

一、面向对象三大特性

  • 封装

    • 将属性和方法书写到类的里面的操作即为封装
    • 封装可以为属性和方法添加私有权限
    • 确保对象中数据的安全
  • 继承

    • 子类默认继承父类的所有属性和方法
    • 子类可以重写父类属性和方法
    • 保证了对象的扩展性
  • 多态

    • 传入不同的对象,产生不同的结果
    • 保证了程序的灵活性

二、类的封装

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 = 500def __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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值