一切皆对象, 定义一个雷,具有属性和方法
class Duck:
def __init__(self,name):
self.name = name
def quack(self):
print(f"Quack! I am {self.name}")
私有属性,python约定 一个下划线开头定义的属性是私有的,self._private ,表示该属性是私有的。
实例的_ _dict_ _ 保存着实例的数据
类的_ _dict_ _ 保存类的方法、文档等
duck = Duck('GuGu')
print(duck.__dict__)
print(Duck.__dict__)
#输出
{'name': 'GuGu'}
{'__module__': '__main__', '__init__': , 'quack': , '__dict__': , '__weakref__': , '__doc__': None}
类方法: @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()
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())
@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
属性的注意事项:如果某些属性的获取比较耗时,通过属性装饰器让对外暴露不太适合。
python的鸭子类型:python的编程风格,鸭子类型不推荐做类型检查,调用只关注是否有某个可以使用的方法,如果没有就会抛出异常,并不是先做类型判断,然后再调用某个需要的方法。
只关心方法,不关心类型。只要实现了某个方法就是他的子类,甚至在定义的时候都没有继承关系。
这么做的好处是代码会非常简洁,符合python的语言设计,但是缺点是大型项目中因为继承的关系,如果鸭子类型使用的比较多,就需要检查对象实例的类型是具体是什么类型。
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
实现了抽象方法就才可以实例化
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的时候会出现,意想不到的情况;