Python进阶

第四章 深入类和对象

4.1 鸭子类型和多态

首先介绍一下多态的概念,多态简单来说就是一个对象具有多种状态,例如猫对象可以看作属于猫这个类,也可以看成是一种动物,因为Java是一门强类型语言,所以多态应用场景比较多,例如,定义一个函数时,我们知道传入的对象是一个Animal类型,我们需要调用其对应的方法,这时候参数就必须是Animal类型,而不能是Cat或者Dog类型。而Python中多态的概念是被弱化了的,可以各自对象调用各自的方法即可。

class Animal(object):
    def say(self):
        print("i am an animal")
class Cat(Animal):
    def say(self):
        print("i am a cat")
class Dog(Animal):
    def say(self):
        print("i am a dog")
class Duck(Animal):
    def say(self):
        print("i am a duck")
def func(obj):
    obj.say()
animal = Animal()
dog = Dog()
cat = Cat()
duck = Duck()
func(animal)
func(dog)
func(cat)
func(duck)
# i am an animal
# i am a dog
# i am a cat
# i am a duck

而鸭子类型是Python中特别重要的概念,简单来说,就是当我们想要知道一个动物是不是鸭子,我们不需要看它是否是鸭子,我们只需要观察如果它的行为(Python中的方法)像鸭子的话,那他就是一只鸭子,这个概念非常核心,可以说是Python的核心之一,下面简单举一个例子。

from collections.abc import *
class Ruler(object):
    def __len__(self):
        pass
r = Ruler()
print(isinstance(r,Sized))

可以看到,Ruler类并没有继承Sized类,但是实例化出来的Ruler对象却是Sized的实例。这就是鸭子类型的神奇之处,鸭子类型会伴随整个学习过程。

4.2 抽象基类(abc模块)

Python中的抽象基类可以类似于Java中的接口,继承对应的类就必须实现其中对应的方法。不然就会报错。

from collections.abc import *
class Ruler(Sized):
    def __len__(self):
        pass
r = Ruler()
class Sized(metaclass=ABCMeta):
    __slots__ = ()
    @abstractmethod
    def __len__(self):
        return 0
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__") # 检查方法
        return NotImplemented # 未完成错误
def _check_methods(C, *methods):
    mro = C.__mro__ # __mro__表示C的继承顺序
    for method in methods:
        for B in mro:
            if method in B.__dict__:
                if B.__dict__[method] is None: # 检查方法
                    return NotImplemented
                break
        else:
            return NotImplemented
    return True

4.3 使用isinstance而不是type

isinctance考虑继承关系,而type考虑的是创建关系,上一章有讲到,isinstance第一个参数尽量使用一个实例化的对象,不要用一个类。当使用类之间的继承关系时,issubclass是一个更好的选择。

class A:
    pass
class B(A):
    pass
b = B()
print(isinstance(b, B))
print(type(b) == B)
print(isinstance(b, A))
print(type(b) == A)
print(issubclass(B,object))
print(isinstance(B,A))
print(issubclass(B,A))
# True
# True
# True
# False
# False
# True

4.4 类变量和成员变量

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 # 试图修改成员变量的值,实际情况是生成了一个新的成员变量,保存值为100,不会影响类变量的值
print(a.x, a.y, a.aa)
print(A.aa)
b = A(3,5)
print(b.aa) # 本身没有aa成员变量,就去类变量中找
# 2 3 100
# 11
# 11

4.5 类属性和实例属性以及查找顺序

查找顺序,首先检查实例中有没有该属性,再去检查类属性,最后考虑继承关系,继承关系满足C3算法。

4.6 静态方法、类方法以及对象方法

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    def tomorrow(self): # 对象方法
        self.day += 1
    @staticmethod
    def parse_from_string(date_str): # 静态方法
        year, month, day = tuple(date_str.split("-"))
        return Date(int(year), int(month), int(day)) @ 1
    @classmethod
    def from_string(cls, date_str): # 类方法
        year, month, day = tuple(date_str.split("-"))
        return cls(int(year), int(month), int(day))  # 2
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)

可以看到类方法比静态方法多传入cls参数,返回对象时可以直接用cls返回,修改类名时这里不用做修改。两者都没有self参数,因此不能访问实例变量。

4.7 数据封装和私有属性

Python中以_开头的属性不希望被用户修改,而以__开头的属性则是私有属性,这是一种规范,但不能保证数据安全,仍可以有其他方式能访问到。

class User:
    def __init__(self, age):
        self.__age = age
    @property
    def age(self):
        #返回年龄
        return self.__age
    @age.setter
    def age(self,value):
        self.__age = value
if __name__ == "__main__":
    user = User(18)
    print(user._User__age)
# 18

虽然无法通过__age方式访问,但是还可以user._User__age去访问。

4.8 Python对象的自省机制

Python对象的自省是通过一定机制访问对象内部结构

class Person:
    """
    人
    """
    name = "user"
class Student(Person):
    def __init__(self, scool_name):
        self.scool_name = scool_name
if __name__ == "__main__":
    user = Student("慕课网")
    # 通过__dict__查询属性
    print(user.__dict__)
    user.__dict__["school_addr"] = "北京市"
    print(user.school_addr)
    print(Person.__dict__)
    print(user.name)
    a = [1,2]
    print(dir(a))
# {'scool_name': '慕课网'}
# 北京市
# {'__module__': '__main__', '__doc__': '\n    人\n    ', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
# user
# ['__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']

4.9 super函数

super执行顺序和属性查找顺序一样,满足C3算法,可以使用__mro__查看执行顺序。

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")
        super(D, self).__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

4.10 Python中的with语句

Python中的with语句一般在读取文件时使用,用于文件关闭,相当于try-except-finally,不过使用起来却方便很多。但其实只要在类中实现__enter__和__exit__用于with语句。

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")
        # return 4
#上下文管理器协议
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()
# enter
# doing something
# exit

4.12 contextlib实现上下文管理器

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")
# file open
# file processing
# file end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值