深入类和对象

 
 
1. 鸭子类型
鸭子类型:多个类都有一个相同的方法,我们可以分别实例化一个对象,定义另外一个函数统一调用多个类中的相同方法
class Person:
    def walk(self):
        print("i walk use my foot")
 
class Bird:
    def walk(self):
        print("i walk use my foot too")
 
jack = Person()
chiken = Bird()
 
def move(arg):
    arg.walk()
 
move(jack)
move(chiken)
输出
i walk use my foot
i walk use my foot too
 
 
2. 抽象基类(abc类),了解就好
特点:抽象基类不能实例化;继承类必须重写抽象基类中的方法
python中有一个函数hasattr(),用于判断对象中是否有某个函数,例如
print(hasattr(b,"__init__")),若返回True,表示b有__init__函数
 
抽象基类用途1:判断对象的类型
isinstance()可用于判断一个对象是否为指定的类型,例如抽象基类Sized
from collections.abc import Sized
isinstance(b, Sized)
 
用途2:强制某个子类必须实现某些方法
 
 
 
3. isinstance 和 type的区别
class A:
    pass
 
 
class B(A):
    pass
 
b = B()
 
print(isinstance(b, B))
print(isinstance(b, A))
print(type(b))
输出:
True
True
<class '__main__.B'>
 
 
 
4. is和"=="
is用于判断2个对象内存地址是否相同,也就是id是否相同;==用于判断2个对象的value值是否相同
a=[1,2,3]
b=[1,2,3]
print(a is b)
print(a==b)
输出
False
True
 
注意:
当对象是比较小的int或者string类型时,为了提高内存利用效率,python采取重用对象内存的办法,此时a is b是True;
当a和b是tuple,list,dict或set型时,python都会给a,b重新分配内存地址,所以a is b为False
 
 
5. 类变量和类的实例变量
class A:
    aa = 1
    def __init__(self, x, y):
        self.x = x
        self.y = y
a = A(2, 3)
A.aa = 11
a.aa = 100
print(a.x, a.y, a.aa)
print(A.aa)
输出
2 3 100
11
说明
aa:是类变量;
self是实例,self.x, self.y 中的x, y为实例变量
A.aa = 11修改了A类的aa属性
a.aa = 100 相当于给__init__()增加了一个变量self.aa=100,并不影响原先类的属性aa
 
 
 
6. 复杂的继承关系中,类和实例属性的查找顺序,可用__mro__函数查找
class D():
    pass
 
class C(D):
    pass
 
class B(D):
    pass
 
class A(B, C):
    pass
 
print(A.__mro__)
输出
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
也就是先查找A中的属性,然后是B, C, D, object
 
 
 
7. 类方法,静态方法,实例方法。静态方法和类方法相当于在__init__之前对数据做一个预处理
class Date:
    def __init__(self, year, month ,day):
        self.year = year
        self.month = month
        self.day = day
 
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
 
    @staticmethod
    def parse_from_string(date_str):
        year, month, day = tuple(date_str.split("-"))
        return Date(int(year), int(month), int(day))
 
    @classmethod
    def from_string(cls, date_str):
        year, month, day = tuple(date_str.split("-"))
        return cls(int(year), int(month), int(day))
 
    @staticmethod
    def valid_str(date_str):
        year, month, day = map(int, date_str.split("-"))
        if year > 0 and month > 0 and month <= 12 and day > 0 and day <= 31:
            return True
        else:
            return False
 
if __name__ == "__main__":
    date1 = Date(2018, 7, 2)
    print(date1)
 
    # 静态方法
    date_str = "2018-12-3"
    date2 = Date.parse_from_string(date_str)
    print(date2)
 
    # 类方法
    date3 = Date.from_string(date_str)
    print(date3)
    
    # 不需要返回类时,使用静态方法好些
    date4 = Date.valid_str("2018-13-2")
    print(date4)
输出
2018/7/2
2018/12/3
2018/12/3
False
 
说明:
1)参数中有self的方法都是实例方法,其中的self表示实例对象
2) 如果 import 一个模块,那么模块__name__ 的值通常为模块文件名,不带路径或者文件扩展名。如果直接运行模块,__name__ 的值等于特别缺省值"__main__"。
3) tuple可以解包
>>> date = "2018-4-5"
>>> date.split("-")
['2018', '4', '5']
>>> year, month, day = tuple(date.split("-"))
>>> year
'2018'
提取字段也可以用map来实现,更方便,map也就是映射,把一组元素按照一个函数映射成另一组元素
>>> year1, month1, day1 = map(int, date.split("-"))
>>> year1
2018
 
4)静态方法的缺陷是函数内return语句要调用类的名字,如果类的名字变化,静态方法也要修改,也就是通常说的硬编码。可使用class method解决这个缺陷,可以说类方法是静态方法的升级版,功能很相似
5)类方法中的参数cls代表类本身,其中cls并不是非要这么写,也可改成其他字符。实例方法中的参数self也是可以修改的
 
 
 
 
8. 数据封装和私有属性
在上面类的基础上新加一个类User, 如下.  实现隐藏具体生日信息,只能得到年龄的数据
class User:
    def __init__(self, birthday):
        self.__birthday = birthday
 
    def get_age(self):
        return 2018 - self.__birthday.year
 
if __name__ == "__main__":
    user = User(Date(1990, 2, 1))
    print(user.get_age())
    print(user._User__birthday)
输出
28
1990/2/1
 
说明:
1)以双下划线开头的变量是私有变量,不能用对象名+变量名直接调用,如果用user.__birthday的话会报错
2)但是可以通过类中的公有函数来调用
3)其实在python内部是把私有变量做了一个变形,"__属性名"变成"_类名__属性名".
 
 
 
9. python对象的自省机制,也就是通过一些方法查询对象的内部结构
class Person:
    name = "jack"
 
class Student(Person):
    """
    继承Person类
    """
    def __init__(self, school_name):
        self.school_name = school_name
 
if __name__ == "__main__":
    user = Student("北大")
 
    # 通过__dict__查询属性
    print(user.__dict__)
    print(Student.__dict__)
 
    # 通过__dict__设置属性
    user.__dict__["school_addr"] = "北京市"
    print(user.school_addr)
 
    # dir()获取对象的方法名和属性名
    print(dir(Person))
    print(dir(user))
输出
{'school_name': '北大'}
{'__module__': '__main__', '__doc__': '\n    继承Person类\n    ', '__init__': <function Student.__init__ at 0x000001C82CC05510>}
北京市
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_addr', 'school_name']
 
说明:
1)__dict__可查看对象的属性和值,同时也能给对象定义新属性和值
2)python中字典是用c编写实现的,很多python的内部结构的数据都是用字典来编写存储的
3)dir()可查看对象的方法和属性名,不能查看其值
4)如果对象不是类,而是列表,那么是不能用__dict__的,只能用dir. 比如a=[1,2], 使用a.__dict__会报错
 
 
 
10. super函数,真的就是调用父类么
class A:
    def __init__(self):
        print("A")
 
class B(A):
    def __init__(self):
        print("B")
        super().__init__()
 
class C(A):
    def __init__(self):
        print("C")
        super().__init__()
 
class D(B, C):
    def __init__(self):
        print("D")
        # 参数中B在C的前面,所以会先调用B
        super().__init__()
 
 
if __name__ == "__main__":
    print(D.__mro__)
    d = D()
输出
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D
B
C
A
说明:
如果super函数就是调用父类,那么输出结果的前三个应该是D, B, A,但经验证是D, B, C, A。 也就是和类的__mro__属性顺序一致
 
 
 
 
11. mixin 模式特点
1) mixin类功能单一
2)不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化成功
3)在mixin中不要使用super这种用法
 
 
 
 
12. with语句--上下文管理器,用于简化程序
 
里面包括两个魔法函数__enter__和__exit__
class Sample:
    def __enter__(self):
        print("enter")
        # 获取资源
        return self
 
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 释放资源
        print("exit")
 
    def do_something(self):
        print("doing sth")
 
with Sample() as sample:
    sample.do_something()
 返回结果
enter
doing sth
exit
 
 python中内置了一个contextmanager,可以把函数变成上下文管理器,进一步简化代码
import contextlib
 
@contextlib.contextmanager
def file_open(filename):
    print("file open")
    yield {}
    print("file end")
 
with file_open("1.txt") as sample:
    print("file processing")
输出结果
file open
file processing
file end
其中yield上面的print相当于__enter__,下面的print相当于__exit__
posted @ 2018-10-31 09:31  坚强的小蚂蚁  阅读(158)  评论(0编辑  收藏  举报