[Python笔记03] 深入类和对象

1.Python中的多态

先来看一段代码

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")

要想调用say方法,一般写法是:

animal1 = Cat()	#实例化一个类
animal1.say()	#调用say方法
animal2 = Dog()
animal2.say()
animal3 = Duck()
animal3.say()

输出结果

I am a cat
I am a dog
I am a duck

接下来我们改写这段代码,把类放进一个列表里,然后去遍历它。

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()

输出结果

I am a cat
I am a dog
I am a duck

这里animal可以指向列表中的任何一个类,而这些类中都实现了say方法,这些类就可以看做是同一类的类,say方法中有不同的实现,这就实现了多态。

使用多态后,方法接收的参数类型可以不同,只要满足父类型即可。

a = ["bobby1", "bobby2"]
b = ["bobby2", "bobby"]
name_tuple = ("bobby3", "bobby4")
name_set = set()
name_set.add("bobby5")
name_set.add("bobby6")


a.extend(b)
print(a)

a = ["bobby1", "bobby2"]
a.extend(name_tuple)
print(a)

a = ["bobby1", "bobby2"]
a.extend(name_set)
print(a)

输出结果

['bobby1', 'bobby2', 'bobby2', 'bobby']
['bobby1', 'bobby2', 'bobby3', 'bobby4']
['bobby1', 'bobby2', 'bobby6', 'bobby5']

可以看到无论是列表、元组还是集合类型,都可以传入extend方法,只要它是一个可迭代对象即可。

再来看到一段代码

a = ["bobby1", "bobby2"]

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)

输出结果

['bobby1', 'bobby2', 'tom', 'bob', 'jane']

company也是一个可迭代对象,所以可以传入extend方法。

2.抽象基类

什么是抽象基类?

  • 在这个类中设定了一些方法,所有继承这个类的子类都必须实现这个方法。
  • 抽象基类不可以实例化。

为什么要用抽象基类?抽象基类的两种应用场景:
(1)检查某个类是否有某种方法

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list
        
    def __len__(self):
        return len(self.employee)
    
com = Company(["bobby1", "bobby2"])
print(hasattr(com, "__len__"))
print(len(com))

输出结果

True
2

这个对象包含__ len __方法,可以对这个对象使用len函数。

(2)判定某个对象的类型

class A:
    pass
    
class B:
    pass

b = B()
print(isinstance(b, A))

输出结果

False

b不是A类型。

有时候我们需要强制某个子类必须实现某些方法,比如说我们实现了一个web框架,继承cache(redis, cache, memorycache),需要设计一个抽象基类, 指定子类必须实现这些方法,如果子类不实现,就会出错。

模拟一个抽象基类
(1)

class CacheBase():
    def get(self, key):
        raise NotImplementedError
        
    def set(self, key, value):
        raise NotImplementedError

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

redis_cache = RedisCache()
redis_cache.set("key", "value")
redis_cache.get("key")

输出结果

    raise NotImplementedError

NotImplementedError

没有实现get方法,抛出了异常。这种方法在使用的时候才会抛出异常。

(2)

import abc
from collections.abc import *

class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self, key):
        pass

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

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

输出结果

    redis_cache = RedisCache()

TypeError: Can't instantiate abstract class RedisCache with abstract methods get

这种方法在初始化的时候,如果没有实现方法,就会抛出异常。

3.isinstance和type

class A:
    pass

class B(A):
    pass

b = B()
print(isinstance(b, B))
print(isinstance(b, A))
print(type(b) is B)
print(type(b) is A)

输出结果

True
True
True
False

isinstance判断对象是不是某种的类型,会根据继承链去找继承关系,type只是判断对象的类型,A和B在内存中是两个不同的对象,因此用type判断得到的只是它们本身的类型。我们推荐使用isinstance来判断对象的类型而不是type。

4.类变量和实例变量

(1)

class A:
    aa = 1	#类变量
    def __init__(self, x, y):
        self.x = x	#实例变量
        self.y = y	#实例变量

a = A(2, 3)
print(a.x, a.y, a.aa)
print(A.aa)

输出结果

2 3 1
1

可以看到a.aa输出了1,说明实例变量在实例中找不到aa变量时,就会向上查找类变量。

(2)

class A:
    aa = 1
    def __init__(self, x, y):
        self.x = x
        self.y = y

a = A(2, 3)
A.aa = 11
print(a.x, a.y, a.aa)
print(A.aa)

输出结果

2 3 11
11

对类变量进行修改,实例对象在查找时也会查找到修改后的类变量。

(3)

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  #赋值给实例属性,新建一个aa属性,放到实例a当中
print(a.x, a.y, a.aa)
print(A.aa)

输出结果

2 3 100
11

使用A.aa时,使用的是类变量,使用a.aa时,会在实例中新建一个aa变量。

5.类变量和实例变量的查找顺序

(1)

class A:
    name = "A"
    def __init__(self):
        self.name = "obj"

a = A()
print(a.name)

输出结果

obj
class A:
    name = "A"
#    def __init__(self):
#        self.name = "obj"

a = A()
print(a.name)

输出结果

A

可以看到查找顺序是由下往上查找。
(2)
在这里插入图片描述
图1的继承顺序

class D:
    pass

class E:
    pass

class C(E):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.__mro__)

输出结果

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)

在查找变量时的查找顺序是A > B > D > C > E

图2的继承顺序

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 > E

6.实例方法、静态方法和类方法

基础代码

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)


if __name__ == "__main__":
    new_day = Date(2020, 5, 1)
    print(new_day)

输出结果

2020/5/1

(1)实例方法

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
        
    def tomorrow(self):	#这是一个实例方法,作用是将实例的属性day的值+1
        self.day += 1
        
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)


if __name__ == "__main__":
    new_day = Date(2020, 5, 1)
    new_day.tomorrow()
    print(new_day)

输出结果

2020/5/2

(2)静态方法

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))
        
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)


if __name__ == "__main__":
    date_str = "2020-5-1"
    new_day = Date.parse_from_string(date_str)	#调用时需要用Date调用,因为方法在属于类
    print(new_day)

输出变量

2020/5/1

(3)类方法

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
        
    def tomorrow(self):
        self.day += 1
        
    @classmethod
    def parse_from_string(cls, date_str):   #cls接收的是类本身
        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__":
    date_str = "2020-5-1"
    new_day = Date.parse_from_string(date_str)
    print(new_day)

输出结果

2020/5/1

(4)静态方法应用场景之一:只需要做判断的时候

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 valid_string(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
        
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)


if __name__ == "__main__":
    date_str = "2020-5-32"
    new_day = Date.valid_string(date_str)
    print(new_day)

输出结果

False

7.数据封装和私有属性

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 2020 - self.__birthday.year
    
if __name__ == "__main__":
    user = User(Date(1999, 4, 8))
    print(user._User__birthday) #python只是将属性名做了变换,并没有绝对私有化
    print(user.get_age())   #只能通过类中的方法访问

输出结果

1999/4/8
21

8.Python对象的自省机制

自省是通过一定的机制查询到对象的内部结构。

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 Person:
    """
    People
    """
    name = "user"

class Student(Person):
    def __init__(self, school_name):
        self.school_name = school_name
        
if __name__ == "__main__":
    user = Student("HNU")
    
    #通过__dict__查询属性
    print(user.__dict__)    #列出对象的属性和值
    user.__dict__["school_addr"] = "长沙市"  #添加一个属性
    print(user.school_addr)
    print(Person.__dict__)
    print(user.name)    #列出类的name属性
    print(dir(user))    #列出对象的所有属性

输出结果

{'school_name': 'HNU'}
长沙市
{'__module__': '__main__', '__doc__': '\n    People\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', 'school_addr', 'school_name']

9.super函数

(1)

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

class B(A):
    def __init__(self):
        print("B")
        super().__init__()
        
if __name__ == "__main__":
    b = B()

输出结果

B
A

先调用B中的__ init ,再调用A中的 init __。
(2)既然我们重写B的构造函数,为什么还要去调用super()?
一个应用场景是在多线程中,实现代码的复用。

from threading import Thread
class MyThread(Thread):
    def __init__(self, name, user):
        self.user = user
        super().__init__(name=name) #直接调用父类的方法,实现代码复用

(3)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")
        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

可以看到和查找继承的顺序是一致的。

10.with语句

(1)try、except、finally

def exe_try():
    try:
        print ("code started")
        raise KeyError
        return 1
    except KeyError:
        print ("key error")
        return 2
    else:
        print ("other error")
        return 3
    finally:
        print ("finally")

print(exe_try())

输出结果

code started
key error
finally
2

代码先执行try中的语句,如果出现KeyError异常,就返回2,出现其他异常返回3,最后执行finally中的语句。这里我们抛出了一个KeyError异常,因此返回2。
我们把下面的语句注释的掉,再运行

#raise KeyError

运行结果

code started
finally
1

可以看到没有异常,返回1。我们把下面这句也注释掉。

#return 1

运行结果

code started
other error
finally
3

返回3,说明执行了else中的语句。
(2)上下文管理器协议
with语句用来管理上下文,可以自动关闭。

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

先进入获取资源,再执行do_something中的代码,最后释放资源。

11.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、付费专栏及课程。

余额充值