第二课 python进阶深入类和对象

第二课 python进阶深入类和对象

tags:

  • Python
  • 慕课网

categories:

  • 鸭子类型
  • 魔法函数

第一节 鸭子类型和多态

  1. wiki定义:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子
  2. 鸭子类型关注点在对象的行为,而不是类型。在 Python 和 Go 中都可以实现鸭子类型。
  3. 举例说明:
class Cat(object):
    def say(self):
        print("i am a cat")


class Dog(object):
    def say(self):
        print("i am a dog")


class Duck(object):
    def say(self):
        print("i am a duck")


# 多态一个接口多种实现 根据传递类型不同 输出不同的结果
animal_list = [Cat, Dog, Duck]
for animal in animal_list:
    animal().say()

a = ["bobby1", "bobby2"]
b = ["bobby0", "bobby"]
name_tuple = ("bobby3", "bobby4")
name_set = set()
name_set.add("bobby5")
name_set.add("bobby6")
# 这里的extend不仅可以传递列表 元组 集合 其他可迭代的对象也可以传入 它内部实际上调用的一个迭代的魔法方法
# 这显然与java语言确定传入确定数据类型的参数不同
a.extend(b)
a.extend(name_tuple)
a.extend(name_set)
print(a)


# 创建一个可迭代的类
class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __getitem__(self, item):
        return self.employee[item]


company = Company(["tom", "bob", "jane"])
a.extend(company)
print(a)

第二节 抽象基类(abc模块)

  1. ABC,Abstract Base Class(抽象基类),主要定义了基本类和最基本的抽象方法,可以为子类定义共有的API,不需要具体实现。相当于是Java中的接口或者是抽象类。
  2. 抽象基类可以不实现具体的方法(当然也可以实现,只不过子类如果想调用抽象基类中定义的方法需要使用super())而是将其留给派生类实现。
  3. ABCMeta 还有一些装饰器:@abstractmethod 和 @abstarctproperty 。abc.ABCMeta 用于在Python程序中创建抽象基类。
    • 如果想要声明“抽象方法”,可以使用 @abstractmethod
    • 如果想声明“抽象属性”,可以使用 @abstractproperty
  4. 抽象基类提供了逻辑和实现解耦的能力,即在不同的模块中通过抽象基类来调用,可以用最精简的方式展示出代码之间的逻辑关系,让模块之间的依赖清晰简单。
  5. 在python中继承某些接口我们推荐用mixin多继承,而不是这种抽象基类,在使用时容易设计过度,不容易理解它。
# 我们需要强制某个子类必须实现某些方法
# 实现了一个web框架,集成cache(redis, cache, memorychache)
# 需要设计一个抽象基类, 指定子类必须实现某些方法
# 如何去模拟一个抽象基类 这里使用的是全局下的abc 而不是collection中的abc
import abc


class CacheBase(metaclass=abc.ABCMeta): # 只能被继承,不能实例化,实例化会报错
    @abc.abstractmethod # 加上装饰器 方法子类必须有这个方法,否则报错
    def get(self, key):
        pass

    @abc.abstractmethod
    def set(self, key, value):
        pass

# 这种方式可以实现抛出异常 但是只有在运行时才可以
# class CacheBase():
#     def get(self, key):
#         raise NotImplementedError
#     def set(self, key, value):
#         raise NotImplementedError


class RedisCache(CacheBase):
    def set(self, key, value):
        pass

    # 没有实现装饰器@abc.abstractmethod的方法 就不能够实例化
    # def get(self, key, value):
    #     pass

# 使用抽象基类 在初始化的时候就会抛异常
redis_cache = RedisCache()
redis_cache.set("key", "value")
  1. 在python实际上已经实现了很多的抽象基类,让我们了解数据结构的一些接口。在from collections.abc import *中
from collections.abc import *


class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __len__(self):
        return len(self.employee)


company = Company(["tom", "bob", "jane"])
print(hasattr(company, "__len__"))
# 这里虽然没有继承Sized 那它怎么判断出它是Sized类型 点进源码看一下
# Lib\_collections_abc.py中可以看到return _check_methods(C, "__len__")
print(isinstance(company, Sized))

第三节 isinstance和type的区别

  1. isinstance:判断该变量是否是该类型,或者是否是该类和该类的父类类型
  2. type():获取该变量名的类型,结合==或is判断该变量的类型是否等于目标类型
class A:
    pass


class B(A):
    pass


b = B()
# 会根据继承链向上找
print(isinstance(b, B)) # True
print(isinstance(b, A)) # True

# is 判断id()对象地址是否相等
# == 判断值是否相等
print(type(b) is B)  # True
print(type(b) is A)  # False

第四节 类变量和实例变量

class A:
    # aa在类中定义 是类变量
    aa = 1
    
    def __init__(self, x, y):
        # self实际上是A的实例,x和y是实例变量
        self.x = x
        self.y = y


a = A(2, 3)
# 修改实例变量
A.aa = 11
# 实例中找不到 就会向上到类中查找aa
print(a.x, a.y, a.aa)
# 在实例上修改aa 这里的aa并不是类变量中的aa 而是创建了一个实例变量aa
a.aa = 100
print(a.x, a.y, a.aa)
print(A.aa)

b = A(3, 5)
print(b.aa)

第五节 类属性和实例属性以及查找顺序

  1. C3算法推荐博客: https://www.jianshu.com/p/a08c61abe895 C3算法从python2.3开始一直到现在
  2. 继承顺序的话,更简单,每个类都有一个.mro()的方法。
# 新式类
class D:
    pass


class E:
    pass


class C(E):
    pass


class B(D):
    pass


class A(B, C):
    pass

# C3算法从python2.3开始一直到现在
print(A.__mro__)

第六节 类方法、静态方法和实例方法

  1. 实例方法就是普通的方法 传self实例
  2. 静态方法用 @staticmethod修饰 不用传self或者cls
  3. 类方法用@classmethod 传递cls 类本身
class Date:
    # 构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    # 实例方法
    def tomorrow(self):
        self.day += 1

    # 静态方法 不需要穿实例self 通过类来调用
    @staticmethod
    def parse_from_string(date_str):
        year, month, day = tuple(date_str.split("-"))
        return Date(int(year), int(month), int(day))

    # 判断是否是合法的时间字符串 这样没有必要返回值 也没有必要穿cls
    @staticmethod
    def valid_str(date_str):
        year, month, day = tuple(date_str.split("-"))
        if int(year)>0 and (int(month) >0 and int(month)<=12) and (int(day) >0 and int(day)<=31):
            return True
        else:
            return False

    # 类方法 传递的类本身cls 解决了上面硬编码的问题 及不论类名是否叫Date都可以返回
    @classmethod
    def from_string(cls, date_str):
        year, month, day = tuple(date_str.split("-"))
        return cls(int(year), int(month), int(day))

    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)


if __name__ == "__main__":
    new_day = Date(2018, 12, 31)
    new_day.tomorrow()
    print(new_day)

    # 2018-12-31
    date_str = "2018-12-31"
    year, month, day = tuple(date_str.split("-"))
    new_day = Date(int(year), int(month), int(day))
    print(new_day)

    # 用staticmethod完成初始化 通过类调用
    new_day = Date.parse_from_string(date_str)
    print(new_day)

    # 用classmethod完成初始化
    new_day = Date.from_string(date_str)
    print(new_day)

    print(Date.valid_str("2018-12-32"))

第七节 数据封装和私有属性

  1. python中通过双下滑线完成私有属性的封装__
  2. 私有属性只能通过类中公共的方法进行访问,无法直接通过实例访问回报错。
  3. 它并不是从语言的层面解决了私有属性的问题,它只是用了一些小技巧, 做了一些变形。 实例._类__属性 依然可以访问到
  4. 其实java中也是可以突破private的,只是稍微麻烦点。
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)


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, 3, 5))
    # 双下划线私有属性不能直接访问 会报错
    # print(user.__birthday)
    # 实际上 它用了小技巧 实例._类__属性 依然可以访问到
    print(user._User__birthday)
    print(user.get_age())

第八节 python对象的自省机制

  1. 自省是通过一定的机制查询到对象的内部结构
  2. __dict__查询属性,这是个效率非常高的函数。通过C语言写的,返回字典
  3. dir 可以列出对象中所有属性,比__dict__更强大,但是没有值,返回数组。
class Person:
    """人"""
    name = "user"


class Student(Person):
    def __init__(self, scool_name):
        self.scool_name = scool_name


if __name__ == "__main__":
    user = Student("QnHyn")
    # 通过__dict__查询属性
    print(user.__dict__)
    user.__dict__["school_addr"] = "北京市"
    print(user.school_addr)
    print(Person.__dict__)
    print(Person.__dict__["__doc__"])
    print(user.name)
    a = [1, 2]
    print(dir(a))

第九节 super函数

  1. 在python2中调用父类方法,super(B, self).init()。但是在python3中已经做了简化直接super().init()即可
  2. super到底执行顺序是什么样的?它并不是调用父类的初始化 而是按照mro的顺序依次调用
from threading import Thread

# 既然我们重写B的构造函数, 为什么还要去调用super?不用自己写逻辑了
class MyThread(Thread):
    def __init__(self, name, user):
        self.user = user
        super().__init__(name=name)


class A:
    def __init__(self):
        print("A")


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


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



class D(B, C):
    def __init__(self):
        print("D")
        super(D, self).__init__()


if __name__ == "__main__":
    # b = B()
    # super到底执行顺序是什么样的?它并不是调用父类的初始化 而是按照mro的顺序依次调用
    print(D.__mro__)
    d = D() # DBCA

第十节 django rest framework中对多继承使用的经验

  1. 不推荐使用多继承可能有奇怪的坑,尽量只继承一个类。建议使用下面minxin模式(混合模式)
  2. mixin模式特点
    • Mixin类功能单一
    • 不和基类关联,可以和任意基类组合, 基类可以不和mixin关联就能初始化成功
    • 在mixin中不要使用super这种用法

第十一节 try用法

  1. finally前面不管有没有运行都会运行后面代码 一般用于资源释放
  2. 如果finally中有return 那么他就会返回finally中的return.如果没有它会返回其他的return
# try except finally
def exe_try():
    try:
        print("code started")
        raise KeyError
        return 1
    except KeyError as e:
        print("key error")
        return 2
    else:
        print("other error")
        return 3
    finally: # 前面不管有没有运行都会运行后面代码 一般用于资源释放
        print("finally")
        # 这里要注意:如果finally中有return 那么他就会返回finally中的return.如果没有它会返回其他的return
        # return 4


if __name__ == "__main__":
    result = exe_try()
    print(result)

第十二节 with的上下文管理器协议

  1. 上下文管理器协议。python是基于协议来编程的。
  2. 通过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 something")


with Sample() as sample:
    sample.do_something()

第十三节 contextlib简化上下文管理器

  1. import contextlib 中的**@contextlib.contextmanager修饰的函数必须是一个生成器**。
  2. @contextlib.contextmanager可以把生成器函数变成上下文管理器。
  3. yield之前的代码 with调用函数之前执行 yield之后的代码 调用完成后执行
import contextlib


@contextlib.contextmanager
def file_open(file_name):
    print("file open")
    yield {}
    print("file end")


with file_open("bobby.txt") as f_opened:
    print("file processing")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值