【Python之路Day8】基础篇之面向对象下篇

今日目录:

类成员

类成员修饰符

特殊的类成员

面向对象相关联的其他知识

异常捕获与处理

设计模式之单例模式

一. 类成员

类的成员有:字段、方法和属性

关系图如下:

1. 字段:

字段分:

  • 静态字段
  • 普通字段

两者在定义和使用上有所区别,如下代码:

class Province:
    contry = '中国'  #静态字段,保存在类中

    def __init__(self,name):
        self.name = name  #普通字段,保存在对象中

在内存中的存储位置是不同的, 静态字段保存在类中, 而普通字段保存在对象中。调用字段:

class Province:
    contry = '中国'

    def __init__(self,name):
        self.name = name


sx = Province('山西')

#访问静态字段和普通字段
# 静态字段存储在类中, 使用类调用, 如 Province.contry
# 普通字段存储在对象中, 由对象调用, 如 sx.name
print('国家: %s
省份: %s'%(Province.contry,sx.name))

#执行结果:
国家: 中国
省份: 山西

 可以看出:

静态字段存储在类中, 只在内存中保存一份;

普通字段存储在每个对象中,需要在每个对象中都保存一份

所以,当类创建时,如果每个对象都具有相同的字段,就使用静态字段。

2. 方法

包括:

  • 普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self
  • 静态方法,属于类,由来调用执行,无默认参数,等同于函数。创建方式:  @staticmethod。
  • 类方法,是静态方法的一种特殊形式。由调用,至少要有一个参数cls,值为类名。创建方式: @classmethod

所有的方法都属于类,只不过调用方式不同。

class Province:
    #静态字段
    country = '中国'

    def __init__(self,arg):
        self.arg = arg

    #普通方法
    def show(self):
        print('普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self!')

    #静态方法
    @staticmethod
    def static_show():
        print('静态方法,属于类,由类来调用执行,无默认参数。创建方式:  @staticmethod')

    @classmethod
    def class_show(cls):
        print('是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod')
        # print(cls)

#调用静态方法,由类调用
Province.static_show()
#调用类方法, 由类调用
Province.class_show()

#调用普通方法,需要先实例化
obj = Province('山西')
obj.show()

#执行结果:
静态方法,属于类,由类来调用执行,无默认参数。创建方式:  @staticmethod
是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod
普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self!



#类方法的参数cls是类本身,也可以在类方法里操作, 如下:
    @classmethod
    def class_show(cls):
        # print('是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod')
        a = cls('核弹')
        a.static_show()


#执行结果:
静态方法,属于类,由类来调用执行,无默认参数。创建方式:  @staticmethod
定义和使用方法

相同点:由于所有的方法都属于类, 所以在内存中存储只保存一份。

不同点:由于各种方法的调用方式不同,调用方法时自动传入的参数不同。

但是有个比较刺激的, 继续上面的例子:

#直接在对象中调用静态方法和类方法
obj = Province('山西')
obj.show()
obj.static_show()
obj.class_show()

#执行结果:
普通方法,由对象调用,至少需要一个self参数。执行普通方法时,self就是对象本身,自动将调用该方法的对象赋值给self!
静态方法,属于类,由类来调用执行,无默认参数。创建方式:  @staticmethod
是静态方法的一种特殊形式。由类调用,至少要有一个参数cls,值为类名。创建方式: @classmethod

so,也是可以被调用的,虽说可以调用,但是不到万不得已,不要用对象来直接调用原属于类该调用的东西。这就是要谈到的要遵循的编程规则了:

一般情况下,自己访问自己的字段,虽说Python没有做严格的限制,但是这是理应遵循的一种编程习惯。

3. 属性

访问属性时和访问字段时一样,不需要加()。 在Python中,属性其实就是普通方法的变种

(1) 基本使用

class C1:
    xxx = '静态字段'
    def func(self):
        print('普通方法')

    #定义一个属性,使用 @property装饰
    @property
    def f1(self):
        print('属性')

#实例化
obj = C1()
#调用普通字段,用类调用
C1.xxx
#调用普通方法, 需要()
obj.func()
#调用定义的属性,直接用调用字段的方式,不用加()
obj.f1


#代码执行结果:
普通方法
属性
class C1:
    xxx = '静态字段'
    def func(self):
        print('普通方法')

    #定义一个属性,使用 @property装饰
    @property
    def f1(self):
        return '属性'  

#实例化
obj = C1()
#调用普通字段,用类调用
C1.xxx
#调用普通方法, 需要()
obj.func()
#调用定义的属性,直接用调用字段的方式,不用加()
result = obj.f1  #如果有return值,需要赋值给变量,而后调用
print(result)
View Code

属性的定义和调用要注意:

定义时,在普通方法上面加 @property 装饰器来装饰

定义时,属性中仅有一个self参数,不能有其他的

调用的时候, 不需要()

属性存在意义是: 访问属性时可以制造出和访问字段相同的假象

属性是普通方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

实例: 对于主机列表页面,每次请求不可能把数据库中的所有的内容显示到页面上,而是通过分页的功能显示,所以,在向数据库请求数据时就需要显式指定获取从第m条到第n条的数据(limit m,n), 这个分页的功能包括:

  • 根据用户请求的当前页和总数据条数计算出m和n
  • 根据m和n去数据库中请求数据
class Pager:
    def __init__(self,curr_num,per_page):
        '''
        构造函数
        :param curr_num:  当前请求的页码
        :param per_page:  每页要显示的数据
        :return:
        '''
        self.curr_num = curr_num
        self.per_page = per_page

    #计算出开始位
    @property
    def start(self):
        #-1就是找出上一页开始位置
        value = (self.curr_num-1) * self.per_page
        return value

    #计算出结束位
    @property
    def end(self):
        value = self.curr_num * self.per_page
        return value

#实例化,传入请求页码和每页显示的数量
#要显示哪页,传入参数即可
P = Pager(1,10)

#起始值
print(P.start)
#结束值
print(P.end)


#执行结果
0
10
实例

(2) 属性的两种定义方式

a. 装饰器方式:

在普通方法的上面添加 @property装饰器的方式

Python中的类有经典类和新式类(python3中全是新式类),新式类的类型比经典类丰富。(如果类继object,那么该类是新式类)

经典类,有一种 @property装饰器

class Pager:
    def __init__(self,curr_num,per_page):
        '''
        构造函数
        :param curr_num:  当前请求的页码
        :param per_page:  每页要显示的数据
        :return:
        '''
        self.curr_num = curr_num
        self.per_page = per_page

    #计算出开始位
    @property
    def start(self):
        #-1就是找出上一页开始位置
        value = (self.curr_num-1) * self.per_page
        return value

    #计算出结束位
    @property
    def end(self):
        value = self.curr_num * self.per_page
        return value

#实例化,传入请求页码和每页显示的数量
P = Pager(1,10)

#起始值
print(P.start)  #自动执行 @property装饰的start方法
#结束值
print(P.end)    #自动执行 @property装饰的end方法
@property

新式类,有三种 @property装饰器,类似于在字典中我们的操作,如:

a = {'k1':'v1'}
print(a.get('k1'))    #获取值
a['k1'] = 'v2'        #设置一个值
del a['k1']           #删除一个key
print(a)
class Pager:
    def __init__(self,curr_num,per_page):
        '''
        构造函数
        :param curr_num:  当前请求的页码
        :param per_page:  每页要显示的数据
        :return:
        '''
        self.curr_num = curr_num
        self.per_page = per_page

    #计算出开始位
    @property
    def start(self):
        #-1就是找出上一页开始位置
        value = (self.curr_num-1) * self.per_page
        return value


    @start.setter
    def start(self,value):
        # value = self.curr_num * self.per_page
        print('你输入的参数是: %s ,这是新修改的值'%value)
        #return value

    @start.deleter
    def start(self):
        print('这里是删除dele动作,你触发了del操作')

#实例化,传入请求页码和每页显示的数量
P = Pager(1,10)

#起始值
result = P.start     #自动执行 @property装饰的start方法
print(result)

P.start = 'shayashi' #自动执行 @start.setter装饰的start方法

del P.start          #自动执行 @start.deleter装饰的start方法


#执行结果:
0
你输入的参数是: shayashi ,这是新修改的值
这里是删除dele动作,你触发了del操作
新式类的三种@property

经典类中的属性只有一种访问方式,对应的是被 @property装饰的方法

新式类中的属性有三种, @property, @方法名.setter,  @方法名.deleter 

b. 静态字段方式

创建值为 property对象的字段, 这种方式经典类和新式类无区别。

class Test:
    def __init__(self,name):
        self.name = name

    def show(self):
        return self.name

    xx = property(show) #注意方法这里不能加(),否则报错

obj1 = Test('daniel')
result = obj1.xx   #自动会调用 show方法,并获取返回值

print(result)
View Code

property构造方法的四个参数:

  • 第一个是fget,值是方法名,调用 对象.属性 属性时自动触发执行
  • 第二个是fset,值是方法名,调用 对象.属性=值 的时候自动触发执行
  • 第三个是fdel,值是方法名,调用 del 对象.属性 的时候自动触发执行
  • 第四个参数是doc, 调用 对象.属性.__doc__ 的时候自动打印,这是该属性的描述信息

以上四种方式中的参数都是可选,可以直接写方法名,但是注意的是,直接写方法名的时候,要对应好位置,比如要写del的一个属性,必须要加上前两个。

class Test:
    def __init__(self,name):
        self.name = name

    def show(self):
        return self.name

    def setter(self,value):
        print('setter')

    def deleter(self):
        print('deleter')

    # xx = property(show,setter,deleter,'这是一段描述信息') #注意方法这里不能加(),否则报错
    xx = property(fget=show,fdel=deleter)  #如果只定义一个get和del的属性,那么就需要使用fget和fdel显式指定了,用普通方法名已经不能满足需求了
obj1 = Test('daniel')
result = obj1.xx   #自动会调用 show方法,并获取返回值

# obj1.xx = 'xxoo'   #自动会调用 setter方法,并获取返回值

del obj1.xx        #自动会调用 deleter 方法,并获取返回值
print(result)
View Code

二. 类成员修饰符

上面介绍了类的成员,这会来看下类成员的修饰符,顾名思义,作用就是来修饰类成员的。 对于每一个类的成员,都有两种形式:

  • 公有成员,在任何地方都可以访问
  • 私有成员,只允许在类的内部访问

私有成员和共有成员的定义不同,私有成员命名时,前两个字符是下划线(特殊成员除外,如__init__, __call__等)。

class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self,name):
        self.name = name   #共有普通字段
        self.__oo = '还不让你看!'  #私有普通字段

私有成员和公有成员的访问限制不同:

1. 字段

静态字段:

  • 共有静态字段:  类可以访问; 类内部可以访问; 子类中可以访问。
  • 私有静态字段:  仅类内部可以访问class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self,name):
        self.name = name   #共有普通字段
        self.__oo = '还不让你看!'  #私有普通字段

    def test(self):
        print(self.__xx)

    def test1(self):
        print(self.__oo)

#静态字段:
print(Foo.country)    #从类里访问静态公共字段,成功
#Foo.__xx             #访问私有静态字段, 失败
  
obj = Foo('dbq')       #在类里面访问静态字段, 成功
obj.test()

obj.test1()                #在类里面访问普通私有字段,成功
class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self):
        self.__oo = '还不让你看!'  #私有普通字段

    def test(self):
        print(Foo.__xx)

    def test1(self):
        print(self.__oo)


class Foo1(Foo):

    def test2(self):
        print(Foo.country)     #子类访问父类公共静态字段, 成功
        print(Foo.__xx)        #子类访问父类私有静态字段, 错误


obj1 = Foo1()   
obj1.test2()

普通字段:

  • 公共普通字段: 对象可以访问, 类内部可以访问,子类(派生类) 中可以访问;
  • 私有普通字段: 仅能从类内部访问;
class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self):
        self.__oo = '私有普通字段,不让你看!'  #私有普通字段
        self.name = 'DBQ'

    def test(self):
        print(self.name)

    def test1(self):
        print(self.__oo)


class Foo1(Foo):
    def test2(self):
        print(self.name)


obj1 = Foo()
print(obj1.name)    #通过对象访问, 成功

obj1.test()         #类内部访问, 成功

obj2 = Foo1()
obj2.test2()        #子类中访问,成功

#执行结果:
DBQ
DBQ
DBQ
公共普通字段
class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self):
        self.__oo = '私有普通字段,不让你看!'  #私有普通字段
        self.name = 'DBQ'

    def test(self):
        print(self.__oo)

    def test1(self):
        print(self.__oo)


class Foo1(Foo):
    def test2(self):
        print(self.__oo)


obj1 = Foo()
print(obj1.__oo)    #通过对象访问, 失败

obj1.test()         #类内部访问, 成功

obj2 = Foo1()
obj2.test2()        #子类中访问, 失败
私有普通字段

注意, 如果要强制访问私有字段,可以通过 [ 对象._类名__私有字段名 ] 来访问,但是强烈不建议强制访问私有成员!

class Foo:
    country = '中国'   #公有静态字段
    __xx = '不给你看!'  #私有静态字段

    def __init__(self):
        self.__oo = '私有普通字段,不让你看!'  #私有普通字段
        self.name = 'DBQ'

    def test(self):
        print(self.__oo)

    def test1(self):
        print(self.__oo)


class Foo1(Foo):
    def test2(self):
        print(self.__oo)


obj1 = Foo()
print(obj1._Foo__oo)    #通过 [对象名._类名__私有字段名] 强制访问成功
#虽说能成功,但是不建议这么访问私有字段!
强制访问私有字段

2. 方法

普通类方法

  • 公共普通类方法:对象可以访问, 子类可以访问
  • 私有普通类方法:仅能从类内部访问
class Foo:
    def show(self):
        print('这是公共普通方法')


class Son(Foo):
    def show1(self):
        print(self.show())

obj = Foo()
obj.show()           #对象访问, 成功
# Foo.show()       #类调用,失败
obj_son = Son()
obj_son.show1()   #子类访问, 成功
公共普通方法
class Foo:
    def __show(self):
        print('这是私有静态方法')

    def show2(self):
        self.__show()


class Son(Foo):
    def show1(self):
        print(self.__show())

obj = Foo()
#obj.__show()        #外部对象访问, 失败

obj.show2()          #类内部访问, 成功

#Foo.show2()         #类调用,失败
obj_son = Son()
obj_son.show1()   #子类访问, 失败
私有普通类方法

静态类方法

  • 公共静态类方法:对象可以访问, 类内部可以访问,子类(派生类) 中可以访问;
  • 私有静态类方法:仅能从类内部访问
class Foo:

    @staticmethod
    def show():
        print('这是共有静态类方法')

    def show2(self):
        self.show()


class Son(Foo):
    def show1(self):
        print(self.show())

obj = Foo()
obj.show()          #外部对象访问, 成功

obj.show2()         #类内部访问, 成功

obj_son = Son()
obj_son.show1()     #子类访问, 成功
公共静态类方法
class Foo:

    @staticmethod
    def __show():
        print('这是私有静态类方法')

    def show2(self):
        self.__show()


class Son(Foo):
    def show1(self):
        print(self.__show())

obj = Foo()
obj.__show()        #外部对象访问, 失败

obj.show2()         #类内部访问, 成功
#
obj_son = Son()
obj_son.show1()     #子类访问, 失败
私有静态类方法

类方法

  • 公共类方法:对象可以访问, 类内部可以访问,子类(派生类) 中可以访问;
  • 私有类方法:仅能从类内部访问
class Foo:

    @classmethod
    def show(cls):
        print('这是类方法')

    def show2(self):
        self.show()


class Son(Foo):
    def show1(self):
        print(self.show())

obj = Foo()
obj.show()            #外部对象访问, 成功

obj.show2()         #类内部访问, 成功
# #
obj_son = Son()
obj_son.show1()     #子类访问, 成功
公共类方法
class Foo:

    @classmethod
    def __show(cls):
        print('这是私有类方法')

    def show2(self):
        self.__show()


class Son(Foo):
    def show1(self):
        print(self.__show())

obj = Foo()
obj.__show()            #外部对象访问,失败

obj.show2()         #类内部访问, 成功
# 
obj_son = Son()
obj_son.show1()     #子类访问, 失败
私有类方法

3. 属性

公共类属性:对象可以访问, 类内部可以访问,子类(派生类) 中可以访问

私有类属性:仅能从类内部访问

class Foo:

    @property
    def show(self):
        print('这是公共属性')

    def show2(self):
        self.show


class Son(Foo):
    def show1(self):
        print(self.show)

obj = Foo()
obj.show            #外部对象访问,成功

obj.show2()         #类内部访问, 成功
# #
obj_son = Son()
obj_son.show1()     #子类访问, 成功
公共类属性
class Foo:

    @property
    def __show(self):
        print('这是公共属性')

    def show2(self):
        self.__show


class Son(Foo):
    def show1(self):
        print(self.__show)

obj = Foo()
obj.__show            #外部对象访问, 失败

obj.show2()         #类内部访问, 成功
# # #
obj_son = Son()
obj_son.show1()     #子类访问, 失败
私有类属性

Ps: 无论方法、属性还是字段,想要强制性的访问私有的话,和上面提到的访问静态私有字段一样,通过:  [ 对象._类名__私有字段名 ], 但是不建议这么操作!

三. 类的特殊成员

类的特殊成员泛指类中的那些两边都有__的成员, 如__init__, __doc__, __dict__等

1. __doc__  类的文档描述信息

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __init__(self):
        pass

print(F1.__doc__)

#代码执行结果:
    这是类的描述信息, 由特殊成员 __doc__调用
__doc__

2. __init__  类的构造方法, 类实例化时自动触发执行。

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __init__(self,name):
        self.name = name
        print(self.name)

obj = F1('DBQ')

#执行代码结果:
DBQ
__init__

3. __module__ 和 __class__

__module__ 当前操作的对象在哪个模块

__class__ 当前操作的对象的类是什么?

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __init__(self,name):
        self.name = name
        # print(self.name)

    def show_info(self):
        print(self.name)
lib/modules.py内容
from lib import modules

obj = modules.F1('DBQ')
print(obj.__module__)   # 查看当前操作的对象属于哪个模块?

print(obj.__class__)       #查看当前操作的对象的类是哪个?


#执行结果:
lib.modules
<class 'lib.modules.F1'>
test.py

4. __del__ 析构方法, 当对象在内存中被释放时候,自动触发执行。

PS:一般无需定义,因为Python是一门高级语言,编程时程序员无需关注内存的分配和释放,Python解释器会自动执行垃圾回收。__del__也就是在垃圾回收机制要回收时,自动触发执行。

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __del__(self):
        pass
__del__

5. __call__  对象后面加括号,触发执行

我们如果要实例化一个类,应该使用 对象 = 类() , 如:

obj = F1()

而对于 __call__方法是 在对象后面加括号触发的,即 对象(), 如:

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __init__(self,name):
        self.name = name
        # print(self.name)

    def __del__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('你是在对象后面加()了,所以才能看到这个__call__定义的信息')

obj = F1('DBQ')
obj()   #对象后加() , 自动触发__call__

#执行结果:
你是在对象后面加()了,所以才能看到这个__call__定义的信息

6. __dict__  类或对象中的所有成员, 以字典的形式返回

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    xx = 'oo'
    def __init__(self,name):
        self.name = name
        # print(self.name)

    def __del__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('你是在对象后面加()了,所以才能看到这个__call__定义的信息')

    def show_info(self):
        print(self.name)

#查看类的成员:
print(F1.__dict__)

#结果:
{'__call__': <function F1.__call__ at 0x1012ef2f0>, 'xx': 'oo', '__dict__': <attribute '__dict__' of 'F1' objects>, '__weakref__': <attribute '__weakref__' of 'F1' objects>, '__doc__': '
    这是类的描述信息, 由特殊成员 __doc__调用
    ', '__del__': <function F1.__del__ at 0x1012ef268>, 'show_info': <function F1.show_info at 0x1012ef378>, '__init__': <function F1.__init__ at 0x1012ef1e0>, '__module__': '__main__'}


#查看对象成员:
obj = F1('DBQ')
print(obj.__dict__)

#执行结果:
{'name': 'DBQ'}



#查看obj2对象的成员
obj2 = F1('Tom')
print(obj2.__dict__)

#执行结果;
{'name': 'Tom'}
__dict__

7. __str__ 如果一个类中定义了 __str__ 方法,那么打印对象时, 默认输出该方法的返回值。

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    xx = 'oo'
    def __init__(self,name):
        self.name = name
        # print(self.name)

    def __str__(self):
        return '你默认是按不到这个信息的,只有定义了__str__你print对象的时候才能看到这个信息!'

obj = F1('DBQ')
print(obj)

#执行代码结果:
你默认是按不到这个信息的,只有定义了__str__你print对象的时候才能看到这个信息!
__str__

8. __getitem__,  __setitem__,  __delitem__ 

用于索引操作, 如字典。 分别表示获取、设置、删除数据。

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __getitem__(self, item):
        print('你执行了get 操作,所以看到了 __getitem__ 定义的print值')

    def __setitem__(self, key, value):
        print('你执行了set 操作,所以看到了 __setitem__ 定义的print值')

    def __delitem__(self, key):
        print('你执行了删除操作,所以看到了 __delitem__ 定义的print值')


obj = F1()
obj['k1']  #get操作, 自动触发__getitem__

obj['k1'] = [1,2,3,4,5]   #set操作,自动触发__setitem__

del obj['k1']   #del操作,自动触发 __delitem__

#执行结果:
你执行了get 操作,所以看到了 __getitem__ 定义的print值
你执行了set 操作,所以看到了 __setitem__ 定义的print值
你执行了删除操作,所以看到了 __delitem__ 定义的print值

9. __getslice__ 、 __setslice__、 __delslice__

__getslice__ , __setslice__ 和 __delslice__ , 主要用于分片操作, 如列表的分片。

注意:python3.x中执行分片,执行的还是__getitem__  __setitem__  __delitem__ 

在2.7中:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
class Foo(object):
 
    def __getslice__(self, i, j):
        print '__getslice__',i,j
 
    def __setslice__(self, i, j, sequence):
        print '__setslice__',i,j
 
    def __delslice__(self, i, j):
        print '__delslice__',i,j
 
obj = Foo()
 
obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__


#代码执行结果:
__getslice__ -1 1
__setslice__ 0 1
__delslice__ 0 2
python2.7

在Python3.x中

class F1:
    '''
    这是类的描述信息, 由特殊成员 __doc__调用
    '''
    def __getitem__(self, item):
        print('你执行了get 操作,所以看到了 __getitem__ 定义的print值')

    def __setitem__(self, key, value):
        print('你执行了set 操作,所以看到了 __setitem__ 定义的print值')

    def __delitem__(self, key):
        print('你执行了删除操作,所以看到了 __delitem__ 定义的print值')

    def __getslice__(self,i, j):
        print(i,j,'__getslice__')

    def __setslice__(self, i, j, sequence):
        print(i,j,sequence,'这是__setslice__')

    def __delslice__(self, i, j):
        print(i,j,'这是__delslice__')

obj = F1()
obj[0:1]         # 将自动触发__getitem__操作

obj[0:1:3] = [1,2]  #自动触发 __setitem__操作
del obj[0:1]     #自动触发__delitem__操作

#代码执行结果:
你执行了get 操作,所以看到了 __getitem__ 定义的print值
你执行了set 操作,所以看到了 __setitem__ 定义的print值
你执行了删除操作,所以看到了 __delitem__ 定义的print值
Python3.x

10. __iter__

迭代器, 元组、字典、列表等之所以可以for循环,是因为内部定义了一个__iter__

class F1:
    pass

obj = F1()

for i in obj:
    print(i)

#代码执行报错:
TypeError: 'F1' object is not iterable
View Code
class F1:
    def __iter__(self):
        pass

obj = F1()

for i in obj:
    print(i)

#代码执行,报错:
TypeError: iter() returned non-iterator of type 'NoneType'
View Code
class F1:
    def __init__(self,num):
        self.num = num

    def __iter__(self):
        return iter(self.num)

obj = F1((1,2,3,4,5))

for i in obj:
    print(i)

#代码执行结果:
1
2
3
4
5
View Code

不难看出,for循环迭代的其实就是 iter((1,2,3,4,5)), 所以,代码可以变成:

obj = iter((1,2,3,4,5))
for i in obj:
    print(i)

#执行结果:
1
2
3
4
5
View Code

11. __new__ 、 __metaclass__

class F1:
    def __init__(self,name):
        self.name = name

    def show_info(self):
        print(self.name)

obj = F1('DBQ')

print(type(obj))

print(type(F1))

#执行代码结果:
<class '__main__.F1'>      #表示 obj对象由 F1类实例化而来   
<class 'type'>                  #表示 F1类由 type 类创建

在上述代码实例中,obj是通过 F1类实例化而来的对象。其实,不仅obj是对象,F1类也是个对象,因为,Python中一切事物皆对象

如果按照这个理论,obj对象是通过执行F1类的构造方法创建而来, F1类对象应该也是通过执行某个类的构造方法创建而来的。

so, obj对象是F1类的一个实例, F1对象是type类的一个实例。即, F1类对象是 通过 type类的构造方法创建。

那么,创建类就可以有两种方式:

(1) 普通方式:

class F1:
    def __init__(self,name):
        self.name = name

    def show_info(self):
        print(self.name)

(2) 特殊方式 (type类的构造方法)

def show_info(self):
    print('DBQ')

F1 = type('F1',(object,),{'show_info':show_info})
#第一个参数: 类名
#第二个参数: 当前类的基类
#第三个参数: 类成员

obj = F1()
obj.show_info()

》》》》类是由 type类实例化而来

那么问题来了,类默认是有 type 类实例化而来,type类中是如何实现的创建类 ? 类又是如何创建对象?

答案: 类中有一个属性 __mataclass__, 其作用是 表示该类是由谁来实例化创建的。 所以, 我们可以为 __metaclas__ 设置一个type 类产生的派生类(子类), 从而查看类创建的过程:

四. 面向对象相关联的其他知识

1. isinstance(obj,cls)

查看某对象是否是某类的实例,返回bool, 继承的父类也为真

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(isinstance(obj,F1))    #查看是否是父类的实例, 为真
print(isinstance(obj,F2))    #查看是否是F2类的实例, 为真

2. issubclass(sub,super)

查看是否是某类的子类

class F1:
    pass

class F2(F1):
    pass

obj = F2()

print(issubclass(F2,F1))   #查看F2是否是F1的子类, 为真
print(issubclass(F1,F2))   #查看F1是否是F2的子类, 为假

3. 对别人的源码扩展

注意一个原则就是,尽量不在源码中修改

class C1:
    def f1(self):
        print('C1.f1')

class C2(C1):
    def f1(self):
        super(C2,self).f1()   #在执行C2代码之前执行C1中的f1方法
        print('C2.f1')

obj = C2()
obj.f1()

#执行结果:
C1.f1
C2.f1

五. 异常捕获与处理

1. 异常的基础

编程中,为了增加友好性, 在程序出现某些问题时,一般不会将错误信息显示给用户,而是提示一个友好的界面,比较直观的例子就是网页中的404请求,大都重定向到一个比较友好的界面。如下代码:

while True:

    num = input('请输入你一个或多个整数: ').strip()

    try:
        num = int(num)
        print('你输入的数字是: %d'%num)
    except Exception:
        print('%s, 你输入的不是一个整数格式!'%Exception)


# 如果输入的是一个整数类型,将返回输入的号码
# 如果输入的是其他的类型,如字符串、浮点数等,会提示用户输入的不是一个整数格式!

####执行结果:
请输入你一个或多个整数: 123
你输入的数字是: 123
请输入你一个或多个整数: a
<class 'Exception'>, 你输入的不是一个整数格式!
请输入你一个或多个整数: 1.
<class 'Exception'>, 你输入

异常是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。异常也是Python对象,表示一个错误。

当Python发生异常时我们需要捕获并且处理它,否则程序会中止执行。

2.异常处理

捕获异常可以使用 try / except语句。try: 用来检测语句块中的错误,从而让 except中语句捕获的异常信息并处理。

语法:

try:
    <语句>     #运行代码
except <名字>:
    <语句>     #如果在try上面的语句出现了 <名字> 异常
except  <名字> ,<数据>:
    <语句>     #如果引发引发了 <名字> 异常,获得附加的<数据>
else:
    <语句>     #如果没有异常发生,有点类似于for,while中的else
finally:
    <语句>     #无论异常与否,最终执行该块

try的工作原理是,当开始一个try语句后,Python就在当前程序的上下文中做标记,当异常出现时就可以回到这里, try子句先执行,接下来的执行流程依赖于执行时是否出现异常?

  • 如果try后的语句执行时发生了异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发别的异常)
  • 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上边(这样将结束程序,并且打印默认的报错信息).
  • 如果在try子句执行时没有发生异常,python将执行else语句后的语句(前提是定义了else子句),然后控制流通过整个try语句
  • 无论try子句执行有无异常发生,python将触发执行finally子句

实例:

打开一个文件,往文件中写入内容,并且没有发生异常:

try:
    f = open('test.txt','w')
    f.write('测试文件,用于测试异常捕获')
except IOError:
    print('Error: 写入失败, 没有找到文件或者权限不足!')
else:
    print('写入成功!')
    f.close()

#执行结果:
写入成功!

#文件内容:
Daniel-Mac:blog daniel$ cat test.txt &&echo
测试文件,用于测试异常捕获

修改文件的权限没有写,而后在打开文件,往文件中写入内容,查看异常:

chmod -w test.txt 
try:
    f = open('test.txt','w')
    f.write('测试文件,用于测试异常捕获')
except IOError:
    print('Error: 写入失败, 没有找到文件或者权限不足!')
else:
    print('写入成功!')
    f.close()

#再次执行代码:
Error: 写入失败, 没有找到文件或者权限不足!

3. 异常种类:

Python标准异常

异常名称描述
BaseException 所有异常的基类
SystemExit 解释器请求退出
KeyboardInterrupt 用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值
GeneratorExit 生成器(generator)发生异常来通知退出
StandardError 所有的内建标准异常的基类
ArithmeticError 所有数值计算错误的基类
FloatingPointError 浮点计算错误
OverflowError 数值运算超出最大限制
ZeroDivisionError 除(或取模)零 (所有数据类型)
AssertionError 断言语句失败
AttributeError 对象没有这个属性
EOFError 没有内建输入,到达EOF 标记
EnvironmentError 操作系统错误的基类
IOError 输入/输出操作失败
OSError 操作系统错误
WindowsError 系统调用失败
ImportError 导入模块/对象失败
LookupError 无效数据查询的基类
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
MemoryError 内存溢出错误(对于Python 解释器不是致命的)
NameError 未声明/初始化对象 (没有属性)
UnboundLocalError 访问未初始化的本地变量
ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError 一般的运行时错误
NotImplementedError 尚未实现的方法
SyntaxError Python 语法错误
IndentationError 缩进错误
TabError Tab 和空格混用
SystemError 一般的解释器系统错误
TypeError 对类型无效的操作
ValueError 传入无效的参数
UnicodeError Unicode 相关的错误
UnicodeDecodeError Unicode 解码时的错误
UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

实例,索引错误:

dic1 = ['tom','jerry']

try:
    dic1[3]
except IndexError:
    print('索引错误啦,哪里有这么多值')

#代码执行结果:
索引错误啦,哪里有这么多值'

实例,key错误

dic1 = {'name':'Tom'}
try:
    dic1['Tom']
except KeyError:
    print('Key错误了,你输入的key不存在!')

#代码执行结果:
Key错误了,你输入的key不存在!
KeyError

实例,ValueError

s = 'hello world'

try:
    float(s)
except ValueError:
    print('值错误!')
else:
    print('值为: %s'%s)
ValueError

"万能异常", 可以捕获到所有的异常情况,Exception:

s = 'hello world'

try:
    float(s)
except Exception:  #各种类型错误都能捕获!
    print('值错误!')
else:
    print('值为: %s'%s)
Exception

但是,Exception语句捕获所有发生的异常,这并不是一个很好的方式,我们不能通过程序识别出具体的异常信息。

所以,我们对特殊处理或提醒异常需要先定义,而后再定义Exception来捕获其他的异常,确保程序能正常运行。

s1 = 'hello world'
try:
    int(s1)
except KeyError:
    print('Error: Key错误!')
except IndexError:
    print('Error: 索引错误!')
except ValueError:
    print('Error: 值错误!')
except Exception:
    print('Error: 出错了!')
else:
    print('你的值是: %s'%s1)

4. 异常的参数

一个异常可以带上参数,可作为输出的异常信息参数。可以通过except语句来捕获异常的参数(该参数在python3中被废弃掉了):

def convert(var):
    try:
        return int(var)
    except ValueError,Argument:
        print('参数没有包含数字
',Argument)

convert('abc')

5. 触发异常

可以使用raise来手动触发异常:

raise语法:

raise [Exception [, args [, traceback]]]

Exception是异常的类型, 如ValueError等,是一个异常参数值。可选,如果不提供,异常参数是 “None”。

最后一个参数是可选的,如果存在,是跟踪异常对象。

try:
    raise Exception('这是手动触发的异常!!')  #触发异常后,后面的代码就不会在执行
except Exception:
    print('手动触发了异常!')

#代码执行结果:
手动触发了异常!

6. 自定义异常:

用户创建一个新的异常类,程序可以命名他们的异常。异常应该是通过直接或间接的方式,继承Exception类。

class MyException(Exception):
    def __init__(self,msg):
        self.message = msg

    def __str__(self):
        return self.message

try:
    raise MyException('错了!')
except MyException:
    print('自己定义的一个异常,触发执行了!')

7. 断言:

断言,能确保代码的正确性。话说回来,如果你确定代码是正确的,那么也就没必要使用断言了,因为它们从来不会运行失败,所以可以直接移除这些断言。如果你确定代码会失败,那么如果不用断言,代码就会通过编译并忽略你的检查。

python 用assert来检查一个条件,如果为真就不做任何事情。如果为假,则会抛出AssertionError的异常信息。

使用语法:

assert 条件

如 :

t1 = ('a','b','c')
try:
    target = input('请输入你一个小写字母:').strip()
    assert target in t1
    print(target)

except AssertionError:
    print(' 错误!')

#执行代码后,如果输入的是a、b、c的话,会直接打印输入的字母,其他值的话,则会抛异常而后被except捕获,打印“错误”

六. 设计模式之单例模式

单例模式,顾名思义,也就是单个实例的意思。

模式特点:保证类仅有一个实例,并提供一个访问它的全局访问点。

# 方法1: __new__方法实现:
# 将类的实例绑定到类变量 _instance , 如果_instance为None说明还没有实例化,那么实例化类,并返回;
# 如果不为 None, 直接返回 cls._instance
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_instance'):
            orig = super(Singleton,cls)
            cls._instance = orig.__new__(cls,*args,**kwargs)

        return cls._instance

class Myclass(Singleton):
    value = 10

a = Myclass()
b = Myclass()

print(a.value)  #查看值

print(id(a))    #查看a的内存地址
print(b.value)
print(id(b))    #查看b的内存地址,两个一致
__new__方法实现
class Singleton:
    __instance = None   #定义一个私有静态字段为初始值

    def __init__(self,name):
        self.name = name

    def show(self):
        print(self.name)
        return 'test_instance'

    @classmethod
    def get_instance(cls):
        if cls.__instance:  #如果字段内有值,直接返回字段值
            return cls.__instance
        else:                     
            obj = cls('DBQ')          #实例化
            cls.__instance = obj    #将对象赋值给字段
            return cls.__instance   #返回对象


a = Singleton.get_instance()
b = Singleton.get_instance()

print(a)
print(id(a))   #内存地址和b相同
print()
print(b)
print(id(b))    #内存地址和a相同
# 后面再来几个对象,也是一样的!

#代码执行结果:
<__main__.Singleton object at 0x101b769b0>
4323764656

<__main__.Singleton object at 0x101b769b0>
4323764656
View Code

单例模式的存在主要是保证当前内存中存在单个实例,避免内存资源浪费。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

from wsgiref.simple_server import  make_server


class DBHelper(object):
    def __init__(self):
        self.hostname = '10.1.1.1'
        self.port = '3306'
        self.username = 'tom'
        self.password = 'sdk23sdf#$_@'

    def select(self):
        '''
        连接数据库,拼接sql语句, 操作
        :return:
        '''
        return 'select'

    def create(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'create'

    def delete(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'delete'

    def update(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'update'

class Handler(object):
    def index(self):
        '''
        创建对象
        :return:
        '''
        db = DBHelper()
        db.select()
        return 'index'

    def news(self):
        '''
        :return:
        '''
        return 'news'

def RunServer(environ, start_response):
    start_response('200 OK',[('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    temp = url.split('/')[1]
    obj = Handler()
    is_exsit = hasattr(obj, temp)  #反射
    if is_exsit:
        func = getattr(obj,temp)
        result = func()
        return result
    else:
        return '404 not found'

if __name__ == '__main__':
    httpd = make_server('',8080, RunServer)
    print('Start HTTP on port 8080...')
    httpd.serve_forever()
Web应用程序实例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: DBQ(Du Baoqiang)

from wsgiref.simple_server import  make_server


class DBHelper(object):

    __instance = None   #先定义一个私有静态字段

    def __init__(self):
        self.hostname = '10.1.1.1'
        self.port = '3306'
        self.username = 'tom'
        self.password = 'sdk23sdf#$_@'

    @staticmethod
    def singleton():
        if DBHelper.__instance:
            return DBHelper.__instance
        else:
            DBHelper.__instance = DBHelper()
            return DBHelper.__instance

    def select(self):
        '''
        连接数据库,拼接sql语句, 操作
        :return:
        '''
        return 'select'

    def create(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'create'

    def delete(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'delete'

    def update(self):
        '''
        连接数据库, 拼接sql语句, 操作
        :return:
        '''
        return 'update'

class Handler(object):
    def index(self):
        '''
        创建对象
        :return:
        '''
        db = DBHelper.singleton()
        print(id(db))
        db.select()
        return 'index'

    def news(self):
        '''
        :return:
        '''
        return 'news'


def RunServer(environ, start_response):
    start_response('200 OK',[('Content-Type', 'text/html')])
    url = environ['PATH_INFO']
    temp = url.split('/')[1]
    obj = Handler()
    is_exsit = hasattr(obj, temp)  #反射
    if is_exsit:
        func = getattr(obj,temp)
        result = func()
        return result
    else:
        return '404 not found'

if __name__ == '__main__':
    httpd = make_server('',8080, RunServer)
    print('Start HTTP on port 8080...')
    httpd.serve_forever()
Web应用实例-单例模式
原文地址:https://www.cnblogs.com/dubq/p/5624369.html