单继承init和多继承init

单继承init

以下两种写法基本无差别:

  • super().init(name)
  • Parent.init(self, name),其中Parent是父类名 ```python class Parent: def init(self, name):
    1. print('parent的init开始被调用')
    2. self.name = name
    3. print('parent的init结束被调用')

class Son(Parent): def init(self, name, age): print(‘Son的init开始被调用’)

    # Parent.__init__(self, name)
    super().__init__(name)
    self.age = age
    print('Son的init结束被调用')

if name == ‘main‘: son = Son(‘Alan’, 28) print(son.name)

‘’’ 输出: Son的init开始被调用 parent的init开始被调用 parent的init结束被调用 Son的init结束被调用 Alan ‘’’

<a name="ZZxhU"></a>
### 多继承__init__

- super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致父类的方法执行多次,所以应尽量避免使用类名的方法调用
- 多继承时,使用super方法对父类传参数,必须把参数全部传递,否则会报错
- 多继承时,使用类名.__init__方法,要把每个父类全部写一遍。而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何使用super方法需要全部传参的一个原因
```python
class Parent:
    def __init__(self, name, *args, **kwargs):
        # 为避免多继承报错,使用不定长参数接受参数
        print('parent的init开始被调用')
        self.name = name  # 初始化name属性
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):
        print('Son1的init开始被调用')
        self.age = age  # 初始化age属性
        super().__init__(name, *args, **kwargs)
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):
        print('Son2的init开始被调用')
        self.gender = gender  # 初始化gender属性
        super().__init__(name, *args, **kwargs)
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')


if __name__ == '__main__':
    gs = Grandson('grandson', 12, '男')
    print('姓名:', gs.name)
    print('年龄:', gs.age)
    print('性别:', gs.gender)

'''
输出:
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
'''
class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self, name)
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self, name)
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        Son1.__init__(self, name, age)
        Son2.__init__(self, name, gender)
        print('Grandson的init结束被调用')


if __name__ == '__main__':
    gs = Grandson('grandson', 12, '男')
    print('姓名:', gs.name)
    print('年龄:', gs.age)
    print('性别:', gs.gender)


'''
输出:
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
'''

两道例题

class Parent(object):
    x = 1

class Child1(Parent):
    pass

class Child2(Parent):
    pass

print(Parent.x, Child1.x, Child2.x)  # 1 1 1
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)  # 1 2 1
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)  # 3 2 3

解释:

  • 在Python中,如果一个变量的名字在当前类中没有找到,将搜索父类直到变量名被找到,如果都没有找到,则报AttributeError异常
  • 如果它的某个子类重写了该值,该值仅仅在当前子类中被改变,不会影响父类 ```python class Parent(object): x = 1

class Child1(Parent): pass

class Child2(Parent): pass

c1 = Child1() c2 = Child2() p = Parent() print(c1.x, c2.x, p.x) # 1 1 1 c1.x = 2 print(c1.x, c2.x, p.x) # 2 1 1 p.x = 3 print(c1.x, c2.x, p.x) # 2 1 3

解释:各实例的变量之间没有关系
<a name="YRpQA"></a>
## property属性

- property属性是一种用起来像是使用实例属性一样的特殊属性,可以对应于某个方法
- 作用:property属性内部进行一系列的逻辑计算,最终将计算结果返回
- property属性的两种方式
   - 装饰器:在方法上应用装饰器
   - 类属性:在类中定义值为property对象的类属性
<a name="APW1m"></a>
### 装饰器方式

- 定义时,在实例方法的基础上添加@property装饰器,并且仅有一个self参数。调用时,无需括号
- 属性有三种访问方式,分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法,可以分别将这三个方法定义为对同一个属性的获取、修改、删除
- 简单举例

![image.png](https://cdn.nlark.com/yuque/0/2022/png/12692524/1655987549882-3cd1c9d2-30cf-4c20-8ae4-663c2bb0ead1.png#clientId=u0fe7b0c8-3b9a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=147&id=axJU2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=221&originWidth=194&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7008&status=done&style=none&taskId=u9ce5aff1-2c6a-4b27-abd8-d6fe953f19a&title=&width=129.33333333333334)
```python
class Goods:
    def __init__(self):
        self.original_price = 100

    @property
    def price(self):
        print('@property')
        return self.original_price

    @price.setter
    def price(self, value):
        print('@price.setter')
        self.original_price = value

    @price.deleter
    def price(self):
        print('@price.deleter')
        del self.original_price


if __name__ == '__main__':
    gas = Goods()
    print(gas.price)
    gas.price = 200
    print(gas.price)
    del gas.price
    # print(gas.price)  # 会报错

'''
输出:
@property
100
@price.setter
@property
200
@price.deleter
'''

类属性方式

  • property方法中有四个参数
    1. 第一个参数是方法名,调用 对象.属性 时自动触发执行该方法
    2. 第二个参数是方法名,调用 对象.属性 = xxx 时自动触发执行该方法
    3. 第三个参数是方法名,调用 del 对象.属性 时自动触发执行该方法
    4. 第四个参数是字符串,是该属性的描述信息,调用 对象.属性.doc时返回该字符串
  • 简单举例

image.png

class Goods:
    def __init__(self):
        self.original_price = 100

    def get_price(self):
        print('@property')
        return self.original_price

    def set_price(self, value):
        print('@price.setter')
        self.original_price = value

    def del_price(self):
        print('@price.deleter')
        del self.original_price

    price = property(get_price, set_price, del_price, '价格属性描述')


if __name__ == '__main__':
    gas = Goods()
    print(gas.price)
    gas.price = 200
    print(gas.price)
    print(Goods.price.__doc__)
    del gas.price

'''
输出:
@property
100
@price.setter
@property
200
价格属性描述
@price.deleter
'''

应用:为私有属性添加getter和setter方法

class Money(object):
    def __init__(self):
        self.__money = 0

    def get_monet(self):
        return self.__money

    def set_money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print('error: 不是整型数字')
class Money(object):
    def __init__(self):
        self.__money = 0

    @property
    def money(self):
        return self.__money

    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print('error: 不是整型数字')


m = Money()
print(m.money)
m.money = 100
print(m.money)
class Money(object):
    def __init__(self):
        self.__money = 0

    def get_money(self):
        return self.__money

    def set_money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print('error: 不是整型数字')

    money = property(get_money, set_money)


m = Money()
print(m.money)
m.money = 100
print(m.money)

魔法属性

  • doc:显示类或方法的描述信息
  • module:显示当前操作的对象在哪个模块
  • class:显示当前操作的对象属于哪个类
  • dict:显示类或对象中的所有属性
  • init:初始化方法,动过类创建对象时,自动触发执行
  • del:当对象在内存中被释放时,自动触发执行,一般无须定义
  • call:对象后面加括号时,自动触发执行
    • 当 对象 = 类名() 时,自动触发执行init方法
    • 当 对象名() 或者 类名()() 时,自动触发执行call方法
  • str:如果一个类中定义了str方法,那么在打印对象时,默认输出该方法的返回值

    with语句

    基本介绍

  • with语句用于异常处理,封装了try…except…finally编码范式,提高了易用性

  • with语句使代码更清晰、更具易读性,它简化了文件流等公共资源的管理
  • 举例

image.png

实现上下文管理器

  • with语句的实现建立在上下文管理器之上,上下文管理器是一个实现enterexit方法的类,任何实现了这两个方法的对象都可以称为上下文管理器,上下文管理器可以使用with语句
  • with语句的执行细节:首先调用enter方法,然后执行with语句中的代码,最后调用exit方法 ```python class MyOpen: def init(self, file_name, open_mode):

      self.file_name = file_name
      self.open_mode = open_mode
    

    def enter(self):

      self.f = open(self.file_name, self.open_mode, encoding='utf8')
      return self.f
    

    def exit(self, exc_type, exc_val, exc_tb):

      print('I will exit')
      self.f.close()
    

with MyOpen(‘outpout.txt’, ‘w’) as f: f.write(‘坚持练习,就是捷径’)

<a name="fEfCA"></a>
### 实现上下文管理器的简便方式
Python提供了一个contextmanager的装饰器,进一步简化了上下文管理器的实现。通过yield将函数分割成两部分,yield之前的语句在__enter__方法中执行,yield之后的语句在__exit__方法中执行,紧跟在yield后面的值是函数的返回值
```python
from contextlib import contextmanager


@contextmanager
def my_open(file_name, open_mode):
    file = open(file_name, open_mode, encoding='utf8')
    yield file
    print('I will exit')
    file.close()


with my_open('output.txt', 'w') as f:
    f.write('坚持练习,就是捷径')