python3知识点杂记(五)

32、新式类与旧式类

新式类与旧式类:是否继承了object基类,python3中默认是新式类,class A() <==> class A(object) <==> class A

mro顺序:新式类是广度优先,从左到右;旧式类是深度优先,从左到右。

class Parent():
    def __init__(self):
        self.a = 1

class Son1(Parent):
    pass

class Son2(Parent):
    def __init__(self):
        self.a = 2
        
class Grandson(Son1, Son2):
    pass

class Grandson2(Son2, Son1):
    pass

gs = Grandson()
gs2 = Grandson2()

print("MRO顺序:\n", Grandson.__mro__)
print("MRO顺序还与继承父类的顺序有关:\n", Grandson.__mro__)
print(gs.a)  # 在新式类里是按照广度优先的搜索方式,按照mro顺序,Grandson中没有属性a,则去Son1中找,Son1中没有则去Son2中找,找到了,返回
MRO顺序:
 (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
MRO顺序还与继承父类的顺序有关:
 (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
2

在旧式类中,mro顺序为:Grandson --> Son1 --> Parent --> Son2,所以gs.a返回1

33、多类继承

多类继承的方式有两种方式:一种是直接 父类名.父类方法 ,一种是super()方法 ,这两种方式各有优势,第一种比较直观简介,但是会出现父类执行多次的情况。第二种方式,虽然不够直观,但是可以保证每个父类的方法之执行一次。

class Parent():
    def foo(self):
        print("I am Parent")
        
class Son1(Parent):
    def foo(self):
        print("I am Son1")
        Parent.foo(self)
        
class Son2(Parent):
    def foo(self):
        print("I am Son2")
        Parent.foo(self)
        
class Grandson(Son1, Son2):
    def foo(self):
        print("I am Grandson")
        Son1.foo(self)
        Son2.foo(self)
        
gs = Grandson()
gs.foo()
I am Grandson
I am Son1
I am Parent
I am Son2
I am Parent

发现Parent父类的foo函数被调用了两次

class Parent():
    def foo(self):
        print("I am Parent")
        
class Son1(Parent):
    def foo(self):
        print("I am Son1")
        # super().foo()
        super(Son1, self).foo()
        
class Son2(Parent):
    def foo(self):
        print("I am Son2")
        super().foo()
        
class Grandson(Son1, Son2):
    def foo(self):
        print("I am Grandson")
        super().foo()
        
gs = Grandson()
gs.foo()
I am Grandson
I am Son1
I am Son2
I am Parent

super方法是根据mro顺序执行的,从gs.foo()开始,先执行Grandson里面的foo函数,当执行到super().foo()时,执行mro顺序表中的Grandson的下一个,即Son1中的foo函数,碰到super函数,再接着执行Son1的下一个中的foo,…,直到最后的父类,这样保证了每个父类的函数只会执行一次。

super()函数继承的形式有两种,super().foo(xx, xxx) super(子类名, self).foo(xx, xxx) ,似乎第一种更好记一些哈。

class Parent():
    def __init__(self, name, *args, **kwargs):    #
        print("Parent的init函数开始调用")
        self.name = name
        print("Parent的init函数调用结束")
        
class Son(Parent):
    def __init__(self, name, age, *args, **kwargs):
        print("Son的init函数开始调用")
        self.age = age
        super().__init__(name, *args, **kwargs)
        print("Son的init函数调用结束")

class Son2(Parent):
    def __init__(self, name, addr, *args, **kwargs):
        print("Son2的init函数开始调用")
        self.addr = addr
        super().__init__(name, *args, **kwargs)
        print("Son2的init函数调用结束")
        
class Grandson2(Son, Son2):
    def __init__(self, name, age, addr):
        print("Grandson2的init函数开始调用")
        super().__init__(name, age, addr)  # 为了避免多继承报错,使用不定长参数,接收参数
        print("Grandson2的init函数调用结束")
        
        
gs2 = Grandson2("zhangsan", 18, "南京")
print("姓名:", gs2.name)
print("年龄:", gs2.age)
print("地址:", gs2.addr)
Grandson2的init函数开始调用
Son的init函数开始调用
Son2的init函数开始调用
Parent的init函数开始调用
Parent的init函数调用结束
Son2的init函数调用结束
Son的init函数调用结束
Grandson2的init函数调用结束
姓名: zhangsan
年龄: 18
地址: 南京

34、单继承中的super

class Parent():
    def __init__(self, name):
        print("Parent的init函数开始调用")
        self.name = name
        print("Parent的init函数调用结束")
        
class Son(Parent):
    def __init__(self, name, age):
        print("Son的init函数开始调用")
        self.age = age
        super().__init__(name)
        print("Son的init函数调用结束")
        
class Grandson(Son):
    def __init__(self, name, age, gender):
        print("Grandson的init函数开始调用")
        self.gender = gender
        super().__init__(name, age)  # 单继承使用super方法对父类传参时不需要传递全部参数,只需要传递父类方法所需的参数
        print("Grandson的init函数调用结束")
        
gs = Grandson("zhangsan", 18, "男")
print("姓名:", gs.name)
print("年龄:", gs.age)
print("性别:", gs.gender)
Grandson的init函数开始调用
Son的init函数开始调用
Parent的init函数开始调用
Parent的init函数调用结束
Son的init函数调用结束
Grandson的init函数调用结束
姓名: zhangsan
年龄: 18
性别: 男
# 面试题:
class Parent():
    x = 1

class Son1(Parent):
    pass

class Son2(Parent):
    pass

print(Parent.x, Son1.x, Son2.x)

Son1.x = 2
print(Parent.x, Son1.x, Son2.x)

Parent.x = 3
print(Parent.x, Son1.x, Son2.x)

1 1 1
1 2 1
3 2 3

35、with与上下文管理器

对于系统资源如文件,数据库连接、socket而言,应用程序打开这些资源后并执行完业务逻辑之后,必须关闭这些系统资源。比如python中打开一个文件,让里面写内容,写完之后,就要关闭该文件,否则极端情况下会出现“too many open files”的错误,因为系统允许你打开的最大文件数量是有限的。同样,对于数据库,如果连接过多而没有及时关闭的话,就会出现“can not collect to MySQL server Too many connection”,因为数据库的连接是非常昂贵的资源,不可能无限制的被创建。

1、如何正确的关闭一个文件?

# 普通版
def f():
    f = open("./log.txt", "r")
    content = f.read()
    print(content)
    f.close()

f()
2019-05-23 19:42:22,366-test.py[line:27]-INFO:这是logging info message
2019-05-23 19:42:22,366-test.py[line:29]-WARNING:这是logging warning message
2019-05-23 19:42:22,366-test.py[line:30]-ERROR:这是logging error message
2019-05-23 19:42:22,366-test.py[line:31]-CRITICAL:这是logging critical message
2019-05-23 19:43:06,568-<ipython-input-12-b21ac2ceac6e>[line:25]-INFO:这是logging info message
2019-05-23 19:43:06,569-<ipython-input-12-b21ac2ceac6e>[line:27]-WARNING:这是logging warning message
2019-05-23 19:43:06,571-<ipython-input-12-b21ac2ceac6e>[line:28]-ERROR:这是logging error message
2019-05-23 19:43:06,572-<ipython-input-12-b21ac2ceac6e>[line:29]-CRITICAL:这是logging critical message

这样存在一个潜在的问题,如果调用read的过程中出现了异常,导致后续代码无法继续执行,close方法将无法被正常使用,因此资源就会一直被该程序占用。

# 进阶版
def f2():
    try:
        f = open("./log.txt", "r")
        content = f.read()
        print(content)
    except IOError:
        print("oops error")
    finally:
        print("不管有没有异常都会执行到这里")
        f.close()

f2()
2019-05-23 19:42:22,366-test.py[line:27]-INFO:这是logging info message
2019-05-23 19:42:22,366-test.py[line:29]-WARNING:这是logging warning message
2019-05-23 19:42:22,366-test.py[line:30]-ERROR:这是logging error message
2019-05-23 19:42:22,366-test.py[line:31]-CRITICAL:这是logging critical message
2019-05-23 19:43:06,568-<ipython-input-12-b21ac2ceac6e>[line:25]-INFO:这是logging info message
2019-05-23 19:43:06,569-<ipython-input-12-b21ac2ceac6e>[line:27]-WARNING:这是logging warning message
2019-05-23 19:43:06,571-<ipython-input-12-b21ac2ceac6e>[line:28]-ERROR:这是logging error message
2019-05-23 19:43:06,572-<ipython-input-12-b21ac2ceac6e>[line:29]-CRITICAL:这是logging critical message

不管有没有异常都会执行到这里
## 高级版
with open("./log.txt", "r") as f:
    content = f.read()
    print(content)
2019-05-23 19:42:22,366-test.py[line:27]-INFO:这是logging info message
2019-05-23 19:42:22,366-test.py[line:29]-WARNING:这是logging warning message
2019-05-23 19:42:22,366-test.py[line:30]-ERROR:这是logging error message
2019-05-23 19:42:22,366-test.py[line:31]-CRITICAL:这是logging critical message
2019-05-23 19:43:06,568-<ipython-input-12-b21ac2ceac6e>[line:25]-INFO:这是logging info message
2019-05-23 19:43:06,569-<ipython-input-12-b21ac2ceac6e>[line:27]-WARNING:这是logging warning message
2019-05-23 19:43:06,571-<ipython-input-12-b21ac2ceac6e>[line:28]-ERROR:这是logging error message
2019-05-23 19:43:06,572-<ipython-input-12-b21ac2ceac6e>[line:29]-CRITICAL:这是logging critical message

with的作用和使用try / finally语句是一样的。

2、上下文管理器

上下文: 在不同的地方表示不同的含义,其实说白了跟文章的上下文是一个意思,可以理解为环境。举个例子,APP点击一个按钮进入一个新的界面,要保存你是在那个屏幕跳过来的等等信息,以便你点击返回时能正确跳转。

上下文管理器: 任何实现了__enter__()和__exit__()方法的对象称为上下文管理器,上下文管理器对象可以使用with关键字。

class File():
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
    
    def __enter__(self):  # 返回资源对象
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f
    
    def __exit__(self, *args):  # 处理一些清除操作
        print("will exit")  
        self.f.close()
        
with File("./log.txt", "r") as ff:  # 1、判断File("./log.txt", "r")是不是上下文管理器; 2、执行__enter__方法返回self.f给ff
    print("reading")
    content = ff.read()             # 3、执行读操作
    print(content)                  # 4、执行完成或出现异常会自动调用__exit__方法

entering
reading
2019-05-23 19:42:22,366-test.py[line:27]-INFO:这是logging info message
2019-05-23 19:42:22,366-test.py[line:29]-WARNING:这是logging warning message
2019-05-23 19:42:22,366-test.py[line:30]-ERROR:这是logging error message
2019-05-23 19:42:22,366-test.py[line:31]-CRITICAL:这是logging critical message
2019-05-23 19:43:06,568-<ipython-input-12-b21ac2ceac6e>[line:25]-INFO:这是logging info message
2019-05-23 19:43:06,569-<ipython-input-12-b21ac2ceac6e>[line:27]-WARNING:这是logging warning message
2019-05-23 19:43:06,571-<ipython-input-12-b21ac2ceac6e>[line:28]-ERROR:这是logging error message
2019-05-23 19:43:06,572-<ipython-input-12-b21ac2ceac6e>[line:29]-CRITICAL:这是logging critical message

will exit

实现上下文管理器的另一种方式,contextmanager装饰器,进一步简化了上下文管理器的实现方式。通过yield将函数分割成两部分,yield之前的语句在__enter__方法中执行,yield后面的语句在__exit__()方法中执行。紧跟yield后面的值是函数的返回值。

from contextlib import contextmanager

@contextmanager
def open_(path, mode):
    f = open(path, mode)  # __enter__方法中执行
    yield f               # 返回资源对象
    f.close               # __exit__方法执行

with open_("./log.txt", "r") as ff:
    content = ff.read()
    print(content)
2019-05-23 19:42:22,366-test.py[line:27]-INFO:这是logging info message
2019-05-23 19:42:22,366-test.py[line:29]-WARNING:这是logging warning message
2019-05-23 19:42:22,366-test.py[line:30]-ERROR:这是logging error message
2019-05-23 19:42:22,366-test.py[line:31]-CRITICAL:这是logging critical message
2019-05-23 19:43:06,568-<ipython-input-12-b21ac2ceac6e>[line:25]-INFO:这是logging info message
2019-05-23 19:43:06,569-<ipython-input-12-b21ac2ceac6e>[line:27]-WARNING:这是logging warning message
2019-05-23 19:43:06,571-<ipython-input-12-b21ac2ceac6e>[line:28]-ERROR:这是logging error message
2019-05-23 19:43:06,572-<ipython-input-12-b21ac2ceac6e>[line:29]-CRITICAL:这是logging critical message

36、封装、继承、多态

1、封装

# 面向过程编程
global_val_1 = 1
global_val_2 = 2
global_val_3 = 3
global_val_4 = 4

def f1():
    pass

def f2():
    pass

def f3():
    pass

# 在实际开发过程中,为了完成较为复杂的任务,往往需要多个函数的配合,当一个函数中收到了
# 数据,为了让其他函数中也能够直接使用,很多人想到使用全局变量实现传递功能,这样很方便,
# 但是存在一个问题,并发程序会出现对同一个全局变量操作的问题。
# 面向对象编程
class Function1():
    class_val_1 = 1
    class_val_2 = 2
    class_val_3 = 3
    
    def f1(self):
        pass

    def f2(self):
        pass

    def f3(self):
        pass
    

class Function2():
    class_val_4 = 1
    
    def f4(self):
        pass

    def f5(self):
        pass


# 面向对象编程,将函数和变量制成一个模板,当需要一个功能时,就提供给需求者。
# 那么需求者就会得到函数+变量(数据),为了使每个需求的人之间不产生误会,因
# 此每个人一份函数(功能)+变量(数据)。

好处:

  • 1、在使用面向过程编程时,当需要对数据进行处理时,需要考虑用哪个模板的哪个函数进行操作,但是当使用面向对象编程时,因为已将数据存储到了这个独立的空间中,这个独立的空间(即对象)中通过一个特殊的变量(__class__)能够获取到类(模板),而这个类中是有一定数量的方法,与此类无关的方法是不会出现在这个类中,一次需要对数据处理时,可以很快的定位到需要的方法,这样更方便。
  • 2、全局变量只能有一份,多个函数就需要多个备份,往往需要利用其它变量进行存储;而通过封装会将用来存储数据的这个变量变为对象中的一个“全局”变量,只要对象不一样那么这个变量就可以再有一份,所以更方便。
  • 3、代码划分更清晰。

2、继承

优点:

  • 1、能够提高代码的重用率,及开发一个类,可以在多个功能中直接使用
  • 2、继承可以有效的进行代码的管理,当某个类有问题只要修改这个类就行,而继承这个类的子类不需要修改

3、多态

子类继承父类,调用某一方法时,如果子类没有对父类的方法进行重写,就调用父类的,如果重写了,就调用子类重写的方法。

未完待续…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值