类的空间问题

添加对象或类的属性

添加对象或类的属性哪里(类__init__方法中、类方法中、类外)都可以添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
def __init__(self,name):
self.name = name

def func(self,sex):
self.sex = sex
# 类外面可以:
obj = A('barry')
obj.age = 18
print(obj.__dict__) # {'name': 'barry', 'age': 18}

# 类内部也可以:
obj = A('barry') # __init__方法可以。
obj.func('男') # func 方法也可以。

对象以及类查询"名字"的顺序

对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->…

类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> …

上面的顺序都是单向不可逆,类名不可能找到对象的属性。

类与类之间的关系

依赖关系

将一个类的对象或者类名传到另一个类的方法使用. 这种关系比较弱。

组合(关联、聚合)关系

关联关系, 其实就是 我需要你. 你也属于我

组合:将一个类的对象封装到另一个类的对象的属性中,就叫组合。

继承

继承分为单继承和多继承

  • python中类的分类

这里需要补充一下python中类的种类(继承需要): 在python2x版本中存在两种类.:
⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object类。

单继承

  1. 类名.对象执行父类方法
  2. 执行顺序
    • 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
    • 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
  3. 同时执行类以及父类方法
    • 子类方法中父类.func(对象,其他参数)
    • super().func(参数)

多继承

一个类继承多个类

  • 经典类

    沿用深度优先算法

    1. 从左至右,依次查找。
    2. 每次都选取节点的最左边,一直找到头,如果没有,返回上一个节点在查询其他路线。
    3. 如果上一个节点没有其他路线或者都已经查询完毕,再返回上一个节点,直至遍历完所有的节点
  • 新式类

    沿用c3算法

    MRO

super()

  • 示例一

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class A:
    def f1(self):
    print('in A f1')

    def f2(self):
    print('in A f2')


    class Foo(A):
    def f1(self):
    super().f2()
    print('in A Foo')


    obj = Foo()
    obj.f1()
  • 示例二

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class A:
    def f1(self):
    print('in A')

    class Foo(A):
    def f1(self):
    super().f1()
    print('in Foo')

    class Bar(A):
    def f1(self):
    print('in Bar')

    class Info(Foo,Bar):
    def f1(self):
    super().f1()
    print('in Info f1')

    obj = Info()
    obj.f1()

    '''
    in Bar
    in Foo
    in Info f1
    '''
    print(Info.mro()) # [<class '__main__.Info'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <class '__main__.A'>, <class 'object'>]
  • 示例三

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class A:
    def f1(self):
    print('in A')

    class Foo(A):
    def f1(self):
    super().f1()
    print('in Foo')

    class Bar(A):
    def f1(self):
    print('in Bar')

    class Info(Foo,Bar):
    def f1(self):
    super(Foo,self).f1()
    print('in Info f1')

    obj = Info()
    obj.f1()

封装

将属性写到__init__

多态

同样名称的方法在不同的子类中会有不同的行为

同⼀个对象, 多种形态. 这个在python中其实是很不容易说明⽩的. 因为我们⼀直在⽤. 只是没有具体的说. 比如. 我们创建⼀个变量a = 10 , 我们知道此时a是整数类型. 但是我们可以通过程序让a = “太白”, 这时, a⼜变成了字符串类型. 这是我们都知道的. 但是, 我要告诉你的是. 这个就是多态性. 同⼀个变量a可以是多种形态。

对类的约束

  1. 提取⽗类. 然后在⽗类中定义好⽅法. 在这个⽅法中什么都不⽤⼲. 就抛⼀个异常就可以了. 这样所有的⼦类都必须重写这个⽅法. 否则. 访问的时候就会报错.
  2. 使⽤元类来描述⽗类. 在元类中给出⼀个抽象⽅法. 这样⼦类就不得不给出抽象⽅法的具体实现. 也可以起到约束的效果.
  • 方式一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Payment:
"""
此类什么都不做,就是制定一个标准,谁继承我,必须定义我里面的方法。
"""
def pay(self,money):
raise Exception("你没有实现pay方法")

class QQpay(Payment):
def pay(self,money):
print('使用qq支付%s元' % money)

class Alipay(Payment):
def pay(self,money):
print('使用阿里支付%s元' % money)

class Wechatpay(Payment):
def fuqian(self,money):
print('使用微信支付%s元' % money)


def pay(obj,money):
obj.pay(money)

a = Alipay()
b = QQpay()
c = Wechatpay()
pay(a,100)
pay(b,200)
pay(c,300)
  • 方式二:引入抽象类的概念
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
# 抽象类 接口类 规范和约束 metaclass指定的是一个元类
@abstractmethod
def pay(self):pass # 抽象方法

class Alipay(Payment):
def pay(self,money):
print('使用支付宝支付了%s元'%money)

class QQpay(Payment):
def pay(self,money):
print('使用qq支付了%s元'%money)

class Wechatpay(Payment):
# def pay(self,money):
# print('使用微信支付了%s元'%money)
def recharge(self):pass

def pay(a,money):
a.pay(money)

a = Alipay()
a.pay(100)
pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能
q = QQpay()
q.pay(100)
pay(q,100)
w = Wechatpay()
pay(w,100) # 到用的时候才会报错

抽象类和接口类做的事情 :建立规范,只要子类继承了我写的这个抽象类,实例化对象时就会报错。

类的私有成员

对于每一个类的成员而言都有两种形式:

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法
类内部派生类
公有类的静态属性可以访问可以访问可以访问
公有方法可以访问可以访问可以访问
私有类的静态属性不可以访问可以访问不可以访问
私有方法不可以访问可以访问不可以访问
对象属性对象类内部派生类
公有对象属性可以访问可以访问可以访问
私有对象属性不可以访问可以访问不可以访问

对于这些私有成员来说,他们只能在类的内部使用,不能再类的外部以及派生类中使用.

类的其他成员

类方法

使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);

调用:实例对象和类对象都可以调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Student:

__num = 0
def __init__(self,name,age):
self.name = name
self.age= age
Student.addNum() # 写在__new__方法中比较合适,但是现在还没有学,暂且放到这里

@classmethod
def addNum(cls):
cls.__num += 1

@classmethod
def getNum(cls):
return cls.__num



a = Student('太白金星', 18)
b = Student('barry', 18)
print(Student.getNum())

静态方法

定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;

调用:实例对象和类对象都可以调用。

静态方法是类中的函数,不需要实例化。可以理解为,静态方法是个独立的、单纯的函数,仅仅托管于某个类的名称空间中,便于使用和维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time

class TimeTest(object):
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second

@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())


print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)

属性

property

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')

@AAA.setter
def AAA(self,value):
print('set的时候运行我啊')

@AAA.deleter
def AAA(self):
print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

#或者:
class Foo:
def get_AAA(self):
print('get的时候运行我啊')

def set_AAA(self,value):
print('set的时候运行我啊')

def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

isinstance

  • isinstance(a,b):判断a是否是b类(或者b类的派生类)实例化的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class A:
    pass

    class B(A):
    pass

    obj = B()


    print(isinstance(obj,B))
    print(isinstance(obj,A))

    isinstance
  • issubclass(a,b): 判断a类是否是b类(或者b的派生类)的派生类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class A:
    pass

    class B(A):
    pass

    class C(B):
    pass

    print(issubclass(B,A))
    print(issubclass(C,A))

    issubclass

反射

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

Python实现自省的函数hasattrgetattrsetattrdelattr

应用于对象的反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Foo:
f = '类的静态变量'
def __init__(self,name,age):
self.name=name
self.age=age

def say_hi(self):
print('hi,%s'%self.name)

obj=Foo('egon',73)

#检测是否含有某属性
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))

#获取属性
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()

print(getattr(obj,'aaaaaaaa','不存在啊')) #报错

#设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))

#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111')#不存在,则报错

print(obj.__dict__)

应用于类的反射

与对象基本相同

反射的作用

更优雅的写代码😆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User:
def login(self):
print('欢迎来到登录页面')

def register(self):
print('欢迎来到注册页面')

def save(self):
print('欢迎来到存储页面')

user = User()
while 1:
choose = input('>>>').strip()
if hasattr(user,choose): #如果user存在choose这个属性
func = getattr(user,choose)#取这个属性并赋值给func变量
func()#执行
else:
print('输入错误。。。。')

异常处理

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

基本语法:

1
2
3
4
try:
被检测的代码块
except 异常类型:
try中一旦检测到异常,就执行这个位置的逻辑

多分支+万能异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
dic = {
1: login,
2: register,
3: dariy,
4: article,
5: comment,
}
print('''
欢迎访问博客园系统:
1,登录
2,注册
3,访问日记页面
4,访问文章页面
5,访问评论页面
''')
try:
choice = int(input('请输入:'))
dic[choice]()
# if choice.isdigit():
# if
#
# else:
# print('请输入数字...')
except ValueError:
print('请输入数字....')
except KeyError:
print('您输入的选项超出范围...')
except Exception as e:
print(e)
    • try…except…else组合

      与循环中的else比较类似,try代码中,只要出现了异常,则不会执行else语句,如果不出现异常,则执行else语句。

      比如我们完成一个转账功能的代码,需要一个转账给另一个人,然后另一个人确认收到才算是转账成功,我们用伪代码写一下,他就可以用在这个地方:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # 伪代码
      try:
      print('扣第一个人钱')
      ...
      print('给第二个人加钱')
      except ValueError:
      print('必须输入数字。。。')

      else:
      print('转账成功')
    • Try…excet…finally组合

      finally这个用法比较有意思,他是在捕获异常发生之前,先执行finally的代码,有点未卜先知的意思。

      • 如果出现异常并且成功捕获了,finally会在try中最后执行。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        try:
        dic = {'name': '太白金星'}
        print(dic[1])

        except KeyError:
        print('出现了keyError错误....')

        finally:
        print('正常执行')
      • 如果出现异常但是没有成功捕获,finally会在异常发生之前执行。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        try:
        dic = {'name': '太白金星'}
        print(dic[1])

        except NameError:
        print('出现了NameError错误....')

        finally:
        print('异常发生之前,先执行我')
      • finally用在哪里呢?

        1. 关闭文件的链接链接,数据等链接时,需要用到finally。
        1
        2
        3
        4
        5
        6
        7
        8
        f = open('file',encoding='utf-8')
        try:
        '''各种操作'''
        print(f.read())
        '''但是发生错误了, 此时没关闭文件句柄,所以'''

        finally:
        f.close()
        1. 函数中,finally也会在return之前先执行。
        1
        2
        3
        4
        5
        6
        def func():
        try:
        return 1
        finally:
        print('finally')
        func()
        1. 循环中,finally也会在return之前执行。
        1
        2
        3
        4
        5
        while 1:
        try:
        break
        finally:
        print('finally')

        finally一般就是收尾工作,在一些重要环节出错之前必须一定要做的比如关闭链接的问题时,最好是用上finally作为最后一道防线,收尾。

  • 主动出发异常

    在类的约束中,我们已经用过此方法,主动发出异常

    1
    raise TypeError('类型错误')
  • 断言

    表示一种强硬的态度,只要assert后面的代码不成立,直接报错,下面的代码就不让你执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # assert 条件

    assert 1 == 1

    assert 1 == 2
    # 应用:
    assert 条件
    代码
    代码
    .......
  • 自定义异常

    python中给你提供的一些错误类型并不是所有的,只是常见的异常,如果以后你在工作中,出现了某种异常无法用已知的错误类型捕获(万能异常只能捕获python中存在的异常),那么你就可以尝试自定义异常,只要继承BaseException类即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class EvaException(BaseException):
    def __init__(self,msg):
    self.msg=msg
    def __str__(self):
    return self.msg

    try:
    raise EvaException('类型错误')
    except EvaException as e:
    print(e)
  • 异常处理正确的使用方式

    有的同学会这么想,学完了异常处理后,好强大,我要为我的每一段程序都加上try…except,干毛线去思考它会不会有逻辑错误啊,这样就很好啊,多省脑细胞===》2B青年欢乐多

    try…except应该尽量少用,因为它本身就是你附加给你的程序的一种异常处理的逻辑,与你的主要的工作是没有关系的
    这种东西加的多了,会导致你的代码可读性变差,只有在有些异常无法预知的情况下,才应该加上try…except,其他的逻辑错误应该尽量修正