8 Python学习记录- 面向对象

一切皆对象, 定义一个雷,具有属性和方法

class Duck:
    def __init__(self,name):
        self.name = name

    def quack(self):
        print(f"Quack! I am {self.name}")

8.1 类的基础知识

1、私有属性

私有属性,python约定 一个下划线开头定义的属性是私有的,self._private ,表示该属性是私有的。

2、实例内容在字典里

实例的_ _dict_ _ 保存着实例的数据

类的_ _dict_ _ 保存类的方法、文档等

duck = Duck('GuGu')
print(duck.__dict__)
print(Duck.__dict__)
#输出
{'name': 'GuGu'}
{'__module__': '__main__', '__init__': , 'quack': , '__dict__': , '__weakref__': , '__doc__': None}

3、类方法

类方法: @classmethod 装饰, 通过类名直接调用,可以返回一个实例。

import random


class Duck:
    def __init__(self, color):
        self.color = color

    def quack(self):
        print(f"Quack! I am a {self.color} duck")

    @classmethod
    def created_random(cls):
         color = random.choice(['yellow', 'white', 'gray'])
         return cls(color=color)

d = Duck.created_random()
d.quack()

4、静态方法

class Duck:
    def __init__(self, color):
        self.color = color

    def quack(self):
        print(f"Quack! I am a {self.color} duck")

    @classmethod
    def created_random(cls):
        color = random.choice(['yellow', 'white', 'gray'])
        return cls(color=color)

    @staticmethod
    def get_sound():
        rand = random.randrange(1, 10)
        return ' '.join(['Meow'] * rand)


print(Duck.get_sound())
d = Duck.created_random()
print(d.get_sound())

5、属性装饰器

@property装饰器可以将类的方法通过属性的方式暴露出来,同时可以对该属性设置setter和deleter方法

import os

class FilePath:
    def __init__(self,path):
        self.path = path

    @property
    def basename(self):
        """获取文件名"""
        return self.path.rsplit(os.sep,1)[-1]

    @basename.setter
    def basename(self,name):
        """修改当前路径的文件名"""
        new_path = self.path.rsplit(os.sep,1)[:-1] + [name]
        self.path = os.sep.join(new_path)

    @basename.deleter
    def basename(self):
        raise RuntimeError('Can not delete basename')


path_obj = FilePath('tmp/test/test.py')
print(path_obj.basename)
path_obj.basename = 'test2.py' # 触发setter方法
print(path_obj.basename)
del path_obj.basename

# 输出
test.py
test2.py
RuntimeError: Can not delete basename

属性的注意事项:如果某些属性的获取比较耗时,通过属性装饰器让对外暴露不太适合。

8.2 Python语言的鸭子类型

python的鸭子类型:python的编程风格,鸭子类型不推荐做类型检查,调用只关注是否有某个可以使用的方法,如果没有就会抛出异常,并不是先做类型判断,然后再调用某个需要的方法。

只关心方法,不关心类型。只要实现了某个方法就是他的子类,甚至在定义的时候都没有继承关系。

这么做的好处是代码会非常简洁,符合python的语言设计,但是缺点是大型项目中因为继承的关系,如果鸭子类型使用的比较多,就需要检查对象实例的类型是具体是什么类型。 

8.3 抽象类

8.3.1  isinstance() 函数

isinstance()来判断对象是不是特定的类型,判断子类是不是属于父类实例。

class Validator:
    def validate(self, value):
        raise NotImplementedError

class NumberValidator(Validator):
    def validate(self):
        pass

print(isinstance(NumberValidator(), NumberValidator))
print(isinstance('Value', Validator))
print(isinstance(NumberValidator(), Validator))

#输出
True
False
True

8.3.2 校验对象是不是某个抽象类的实例

from collections.abc import Iterable


class Factory:
    def __init__(self, value):
        self.value = value

    def __iter__(self):
        pass


print(isinstance(Factory(2), Iterable))
# 输出
True

Factory类的实例并没有继承 Iterable 类,但是通过isinstance方法判断返回True,为什么呢?

这是因为抽象类Iterable的 _ _ subclasshook_ _ 方法:重新定义是不是其子类的判断方式,只要实现了_ _iter_ _ 魔法方法的类都是 Iterable类的实例。

还有一种方法是通过抽象类的 register() 方法来注册子类,实现了继承。通过isinstance方法判断也是返回True

from abc import ABC


class Factory(ABC):
    def __init__(self, value):
        self.value = value

class Foo:
    pass

print(isinstance(Foo(), Factory))
Factory.register(Foo)
print(isinstance(Foo(), Factory))
# 输出
False
True

抽象类定义定义必须实现的方法:

from abc import ABC, abstractmethod

class Validator(ABC):

    @abstractmethod
    def validate(self, vale):
        raise NotImplementedError

class InvalidValidator(Validator):
    def __init__(self, value):
        pass

validator = InvalidValidator(10)

# 输出
TypeError: Can't instantiate abstract class InvalidValidator with abstract method validate

实现了抽象方法就才可以实例化

8.4 多重继承与MRO:

class A:
    def say(self):
        print("I am A")


class B(A):
    pass


class C(A):
    def say(self):
        print("I am C")


class D(B, C):
    pass

D继承了 B和C,B和C是A的子类,B没有重写say,C重写了say,D.say() 应该输出?

D().say()
print(D.mro())
# 输出
I am C
[, , , , ]

python使用了MRO算法,查看MRO算法,查找的顺序是DBCA,找到了就直接返回。

 

class A:
    def __init__(self):
        print("I am A")
        super(A, self).__init__()

class B(A):
    def __init__(self):
        print("I am B")
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print("I am C")
        super(C, self).__init__()

class D(B,C):
    pass


d = D()
# 输出
I am B
I am C
I am A

通过输出可以看到:D实例初始化的时候,先执行B的初始化,B初始化方法继承了A但是为什么输出了C,然后C初始化方法中再输出A?

因为多重继承关系super调用的不是父类A的初始化方法了。而是按照MRO算法: DBCA的顺序调用的。

所以多重继承在写代码的实际上应该避免,因为这可能出现一些不可预知的情况,子类调用super的时候会出现,意想不到的情况;

你可能感兴趣的:(Python学习笔记,python)