面向对象 (二)

内置方法 (一)

  • 当对对象按照字典操作时,会自动触发相关方法
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class Person:
    # 当对象按照字典设置键值对时,会自动触发该方法
    def __setitem__(self, key, value):
    # print(key, value)
    self.__dict__[key] = value

    # 当对象按照字典操作根据键获取值时,会自动触发该方法
    def __getitem__(self, item):
    print(item)
    return self.__dict__[item]

    # 当做字典操作,删除键值对时,会自动触发该方法
    def __delitem__(self, key):
    del self.__dict__[key]


    p = Person()

    p['name'] = 'xiaoming'
    print(p.name)
    # 通过字典方式添加的键值对,可以通过属性的方式获取
    # print(p['name'])
    # print(p.__dict__)

    del p['name']
    # print(p.name)

类的继承

  • 相关概念:

    • 继承:父类的属性和方法子类可以直接拥有
    • 派生:子类在父类的基础上衍生出来的新的特征(属性和方法)
    • 总结:其实他们是一回事,只是描述问题的角度侧重点不同(继承描述相同点,派生侧重不同点)
  • 继承:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # class Animal(object):

    # 当没有指定父类时,相当于默认父类为object
    class Animal:
    def __init__(self, name):
    self.name = name

    def eat(self):
    print('小动物一天到晚吃个不停')


    # 继承自Animal
    class Dog(Animal):
    pass


    d = Dog('旺财')
    # 直接拥有父类的方法
    d.eat()
    # 直接拥有父类的属性
    print(d.name)
  • 派生:子类可以扩充属性和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Animal:
    def run(self):
    print('小动物喜欢到处乱跑')


    class Rabbit(Animal):
    # 添加方法
    def eat(self):
    print('爱吃萝卜和青菜')


    xiaobai = Rabbit()
    xiaobai.run()
    xiaobai.eat()
    # 添加属性
    xiaobai.color = 'white'
    print(xiaobai.color)
  • 重写:

    • 父类的方法完全不合适,子类需要全部的覆盖
    • 父类的方法合适,但是需要完善
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      class Animal:
      def run(self):
      print('小动物喜欢到处乱跑')

      def eat(self):
      print('小动物也是一天三顿')


      class Cat(Animal):
      # 当父类的方法完全不合适,可以覆盖重写
      def run(self):
      print('俺走的是猫步')

      # 父类的方法合适,但是子类需要添加内容完善功能
      def eat(self):
      # 调用父类方法,不建议使用
      # Animal.eat(self)
      # super(Cat, self).eat()
      # 类名及self可以不传
      super().eat()
      print('不过俺喜欢吃鱼')


      jiafei = Cat()
      jiafei.run()
      jiafei.eat()
  • 多继承:

    • 概念:一个类可以有多个父类
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class A:
      def eat(self):
      print('eat func of A')


      class B:
      def eat(self):
      print('eat func of B')


      # 多继承,多个父类使用逗号隔开
      class C(B, A):
      def eat(self):
      # print('eat func of C')
      # 当多个父类拥有相同方法时,会按照继承的先后顺序进行选择
      # super().eat()
      # 如果非要使用后面的类的方法时,可以明确指定进行调用
      A.eat(self)


      c = C()
      c.eat()

访问权限

  • 权限:

    • 公有的:类中的普通属性和方法,默认都是公有的,可以在类的内部、外部、子类中使用
    • 私有的:定义时在前面加两个 _,只能在本类的内部使用,不能在外部及子类中使用
  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    class Person:
    def __init__(self, name, age):
    self.name = name
    # 在属性的前面添加两个'_',外部不能访问,系统内部的除外
    # 默认的属性名:__age => _Person__age
    self.__age = age

    def eat(self):
    print('民以食为天')

    # 在方法的前面添加两个'_',外部不能访问,系统内部的除外
    # 默认的方法名:__inner() => _Person__inner()
    def __inner(self):
    print('私有方法,不想让外部调用')

    def test(self):
    # 在类的内部可以访问私有的属性和方法
    print(self.__age)
    self.__inner()


    p = Person('老王', 50)
    # 默认所有的属性和方法都是共有的,就是在类的外面可以直接使用
    p.eat()
    print(p.name)

    # 以下两局会报错,提示没有相关的属性或方法
    # p.__inner()
    # print(p.__age)

    print(p.__dict__)
    # 可以通过系统修改的名称找到,但是强烈建议不要这样使用
    print(p._Person__age)
    p._Person__inner()
    p.test()


    class Man(Person):
    def test(self):
    # print(self.__age)
    # self.__inner()
    print('子类无法使用父类的私有属性和方法')


    m = Man('木盖', 24)
    m.test()

类属性

  • 说明:定义时,写在类中,但是方法外的属性,通常放在类的开头位置

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Person:
    # 定义类属性
    nation = '中国'

    def __init__(self, name):
    self.name = name
    self.nation = 'china'


    # 通过类名访问类属性
    print(Person.nation)

    # 通过对象也可以访问类属性,但是不建议
    # 当对象有同名的成员属性时,使用的就是成员属性
    p = Person('xiaoming')
    print(p.nation)

    # 也可以动态添加
    Person.hello = 'hello'
    print(Person.hello)

    # 特殊的类属性
    # 表示类名字符串
    print(Person.__name__)

    # 表示父类构成的元组
    print(Person.__bases__)

    # 存储类相关的信息
    print(Person.__dict__)

类方法

  • 说明:

    • 定义时使用装饰器:classmethod
    • 通过类名调用
  • 作用:

    • 可以创建对象或者简洁的创建对象
    • 对外提供简单易用的接口
  • 示例 1:语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Preson:
    def eat(self):
    print('麻辣是我的最爱')

    @classmethod
    def test(cls):
    print('类方法 test')
    # cls 表示当前类
    print(cls)

    @classmethod
    def create(cls):
    p = cls()
    # 进行特定的初始化设置
    p.age = 1
    return p


    Preson.test()
    print(Preson)
    # 可以创建对象或者简洁的创建对象
    p = Preson.create()
    print(p)
  • 示例 2:设计一个数字类,有两个属性,能够进行加减乘除运算,要求:计算两个数的平方和

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    class Number:
    def __init__(self, num1, num2):
    self.num1 = num1
    self.num2 = num2

    def add(self):
    return self.num1 + self.num2

    def sub(self):
    return self.num1 - self.num2

    def mul(self):
    return self.num1 * self.num2

    def div(self):
    if self.num2 == 0:
    return 0
    return self.num1 / self.num2

    @classmethod
    def pingfanghe(cls, num1, num2):
    # 第一个数
    n1 = Number(num1, num1)
    # 求平方
    n12 = n1.mul()

    # 第二个数
    n2 = Number(num2, num2)
    # 求平方
    n22 = n2.mul()

    # 第三个数
    n3 = Number(n12, n22)
    # 求和
    return n3.add()


    print(Number.pingfanghe(3, 4))

静态方法

  • 说明:

    • 使用装饰器:staticmethod 进行修饰
    • 定义的方法没有第一个表示当前类的参数
  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Person:
    @staticmethod
    def test():
    print('static method test')

    @staticmethod
    def create():
    # 也可以创建对象
    p = Person()
    return p


    Person.test()

    p = Person.create()

    与类方法相比:

    1
    2
    1. 除了装饰器不同,其他的基本一样,做与对象创建无关的任务时可以使用静态方法
    2. 所有静态方法能够完成的功能都可以使用类方法完成

多态特性

  • 定义:不同的对象,调用相同的方法有不同的响应

  • 体现:多态性,不同的对象接收相同的消息会有不同的响应

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Animal:
    def run(self):
    pass


    class Dog(Animal):
    def run(self):
    print('狗奔跑的时候一般是s型')


    class Cat(Animal):
    def run(self):
    print('猫平时走猫步,偶尔会突然加速')


    # 参数必须有run方法
    def func(obj):
    obj.run()


    func(Dog())
    func(Cat())

属性函数

  • 说明:

    • 使用装饰器:property 进行修饰
    • 将方法当做属性一样使用
  • 作用:保护特定属性,处理属性

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class User:
    def __init__(self, username, password):
    self.username = username
    self.__password = password

    # 使用property修饰的方法,可以当做属性访问
    # 可以保护特定的属性
    @property
    def password(self):
    print('有人想直接获取密码')
    return '哈哈,想偷看,没门'
    # return self.__password

    # 当设置password属性时,会自动调用该方法
    @password.setter
    def password(self, password):
    print('注意了,有人想修改密码', password)
    # 可以对密码进行特定处理,然后保存
    self.__password = 'xxx' + password + 'yyy'


    u = User('xiaoming', '123456')
    print(u.username)
    # 直接访问密码是不需要的,也是不安全的
    # 直接访问password属性,自动调用使用property装饰器修饰后的password方法
    print(u.password)
    u.password = '654321'

内置方法 (二)

  • 说明:

    • 把对象当做函数一样调用时,系统会自动触发__call__方法
    • 若想支持这样的操作,需要在类中提供__call__方法
  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Person:
    # 把对象当做函数一样调用时,系统会自动触发该方法
    def __call__(self, *args, **kwargs):
    # 如计算和
    return sum(args)


    p = Person()
    # 将对象当做函数使用,需要提供__call__方法
    ret = p(1, 2, 3, 4, name='xiaoming')
    print(ret)

函数判断

  • 问题:如何判断一个对象是否可以像函数一样调用

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class A:
    def __call__(self, *args, **kwargs):
    print('xxx')


    def test():
    pass


    # 不能使用isinstance方法判断一个对象是否是函数
    # print(isinstance(test(), function))
    # 打印时仍然是function类型
    # print(type(test))

    # 判断一个对象是否可以像函数一样调用
    print(callable(test))
    test()

    a = A()
    print(callable(a))
    # a()


    # 判断对象是否拥有__call__属性
    print(hasattr(test, '__call__'))
    print(hasattr(a, '__call__'))

    # 判断是否是函数
    from inspect import isfunction

    print(isfunction(test))
    print(isfunction(a))

数据持久化存储

  • 方法:普通文件、数据库、序列化

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import pickle


    class Person:
    def __init__(self, name, age):
    self.name = name
    self.age = age


    # 存储:对象 => 文件
    # xiaoming = Person('小明', 18)
    # fp = open('test.txt', 'wb')
    # pickle.dump(xiaoming, fp)
    # print('对象数据已存储完毕')

    # 读取:文件 => 对象
    fp = open('test.txt', 'rb')
    xiaoming = pickle.load(fp)

    print(xiaoming.name)
    print(xiaoming.age)

    fp.close()