python培训Day8 随笔

本周先继续介绍了面向对象的两个特殊的方法


一、面向对象相关


1、isinstance(obj, cls)

它的作用就是检查某个对象obj是否被指定类创建,如果是就返回True,不是就是返回False。

例如,c是有A()类创建的实例

c是有A()类创建的实例

class A(object):
    pass
#c是有A()类创建的实例
c=A()
print isinstance(c,A)

True

如果B()类继承了A()类,c是由B()类创建的。那么c即是B()类创建的也同时是A()类创建的,所以返回的都是True

class A(object):
    pass
class B(A):
    pass
#c是由B()类创建的实例
c=B()
print isinstance(c,B)
print isinstance(c,A)

True
True

那这个功能到底有什么用呢?我们知道python中有很多的数据类型。例如str,dic,list,int等等。在python的世界里有一句话“一切皆为对象”,对象又是通过类创造出来的。那么起始str,dic这些对象都是由一个type基类创造出来的。字符串、字典的类先要继承基类type(),然后才能创队创造对象。说了这么多绕弯的话,其实就是想告诉大家,因为列表、字典也是类创造出来的对象,所以也适用于isinstance()方法。用来判断传过来的数据是不是指定的数据类型。记住最后一句就行了

例如,传过来的是123。但是我们并不知道是str还是int,那怎么办?通过isinstance()的返回的值

我们就能知道数据的类型了,这个方法以后编程会大量用到。

s=123
if isinstance(s,str):
    print 'This string'
if isinstance(s,int):
    print 'This int'
    
This int


2、issubclass(sub, super)

这个方法的作用就是判断两个类是否是基类和派生类的关系,如果是就返回True,不是就返回False

这个方法用的不多,知道就可以了

class A(object):
    pass
#B()类继承了A()
class B(A):
    pass
#C类和A、B都没有关系
class C(object):
    pass
print issubclass(B,A)
print issubclass(C,A)

True
False


二、异常的处理


1、异常的基础

python中经常会出现各种各样的错误异常,比如要求输入的数据类型是数字,但是我输入的是字符串就会引发TypeError错误。

num=raw_input('please input number:')
print num+1
please input number:3
Traceback (most recent call last):
  File "F:/python_file/test.py", line 4, in <module>
    print num+1
TypeError: cannot concatenate 'str' and 'int' objects

如果不想看到这样的错误信息,其实我们可以手动捕获异常然后打印出发到指定错误之后的错误信息

try:
    num=raw_input('please input number:')
    print num+1
except TypeError,e:  #except后面的参数就是要捕获的异常类型,e里面包含的是系统默认的该种异常说明
    print e  #打印出来系统对该种异常的说明
please input number:3
cannot concatenate 'str' and 'int' objects

e这个参数其实也不是必须的,如果我不想使用系统默认的说明。我们也可以自己定义触发到指定异常之后屏幕上到底打印什么

try:
    num=raw_input('please input number:')
    print num+1
except TypeError:
    print 'you input wrong type'
please input number:3
you input wrong type


2、异常的种类

上面看到的TypeError只是能够触发python异常的其中一种而已,那python中到底有多少个可以触发异常的错误呢?请看下表

ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

看着是不是有点吓人?这么大一堆,其实数量虽然多但是我们经常碰到的也就是下面这几种

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

我们通过捕获上述的异常,就在出现相应的错误时候进行相应的处理。例如运行程序的时候用户输入

Ctrl+C按键退出程序,如果我们能捕获KeyboardInterrupt这个异常,就可以让用户退不出去

import time
for i in range(10):
    try:
        print i
        time.sleep(1)
    except KeyboardInterrupt:
        print "I cant`t stop it"
        continue
        
0
1
2
I cant`t stop it
3
4


3、万能异常

这么多种类的异常肯定不能在定义的时候全都写上,这时候就可以使用万能异常Exception。这个万能异常可以捕获所有的异常。这样就不用每个异常都输入一遍了。大概的结构如下

try:
    pass
except Exception,e:
    print e


4、自定义异常

python虽然提供了很多种的异常,但是有时候我们可能根据业务需要有自定义异常的需求,那这个自定义的异常怎么写呢,示例如下:

class my_error(Exception):#要自己先定义一个类,这个类必须继承(Exception)
    def __init__(self,msg=None):
        self.msg=msg
    """
    __str__方法是提供给print打印时候用到的,所有打印的结果都是调用了__str__方法的结果
    """
    def __str__(self): #定义触发异常后 print e时候打印出来的内容
        if self.msg:
            return self.msg
        else:
            return 'my define error'

这样就定义好了一个我们自己的异常,只要出发了这个异常就会显示我们定义的内容。但是问题来了,我们怎么才能出发自己的异常。答案就是在try下面通过关键字raise,完整的代码如下:

class my_error(Exception):#要自己先定义一个类,这个类必须继承(Exception)
    def __init__(self,msg=None):
        self.msg=msg
    """
    __str__方法是提供给print打印时候用到的,所有打印的结果都是调用了__str__方法的结果
    """
    def __str__(self): #定义触发异常后 print e时候打印出来的内容
        if self.msg:
            return self.msg
        else:
            return 'my define error'
try:
    raise my_error()#通过关键字raise 触发我们自定义的异常。这个办法也可以用来触发系统自带的各种异常
except Exception,e: #这个e就相当于e=my_error.__str__(),本例中获取的就是用户输入的信息或'alex error'
    print e
my define error


4、异常的其他结构

虽然有了异常,但是代码不可能每次运行都会触发到。python的异常提供了一个代码结构,用这个结构写的话代码会显得清晰点,当然如果不用也可以实现功能。按照老师的话讲就是提升逼格的神器,用异常实现数据库连接的逻辑示例如下:

try:
    #逻辑代码  连接数据局,执行sql
    pass
except IndexError,e: #定义不同类型的异常
    pass
except KeyError,e:
else:
    #逻辑代码块未见异常
    pass
finally: 
    #断开连接,释放资源
    #永远执行,逻辑代码执行完之后
    pass

这个结构的运行的逻辑流程图如下:

wKioL1ZuhwbCII98AACKBtHsbg4094.jpg






三、反射

反射其实就是在内存中操作元素的一组方法的集合


先说举个例子,假设我们有2个文件。一个main.py的主文件,一个是home.py用于存放各种模块。

先看home.py的源代码

def home():
    return 'home.home'
def etc():
    return 'home.etc'
def usr():
    return 'home.usr'
def var():
    return 'home.var'
def log():
    return 'home.log'

如果我们要在main.py文件中根据用户输入的字符串去调用home.py里面的函数,就需要在main.py中用如下方式

import home
url=raw_input('url >>>')
if url=='home':
    ret=home.home()
    print ret
if url=='etc':
    ret=home.etc()
    print ret
if url=='usr':
    ret=home.usr()
    print ret
if url=='var':
    ret=home.var()
    print ret
if url=='log':
    ret=home.log()
    print ret

如果home.py里面有1000个函数,那么在main.py文件里也得写1000个if。是不是很麻烦?而且还得保证一个if都不能少,否则就找不到home里的函数了。这个low的办法肯定是不可行的。所以这就引出了反射的第一个方法getattr()方法


1、getattr()

这个方法的作用就是去某个容器(模块)中,找函数,字符串函数名,如果存在就返回这个函数,如果找不到则触发attributeError异常

那么还是刚才那个需求,用getattr()怎么做呢?看代码

import home
url=raw_input('url >>>')
res=getattr(home,url)
print res()

url >>>usr
home.usr

看!代码量大幅缩减但是功能是一样的。这就是getattr()的优势。

要注意 url参数传入的可是个str字符串,由此可知getattr()方法以字符串的形式执行函数

现在我们修改一下代码,让用户输入的时候把模块名和函数放在一起,以'/'号相连类似于linux下的路径的格式输入

import home
url=raw_input('url >>>')
"""
将分割出来的列表第二部分,也就是函数名赋值给func
"""
func=url.split('/')[1]
res=getattr(home,func)
print res()

这样子程序就看着智能化一点了,但是目前getattr()方法的第一个参数home还是写死的,如果我要根据输入引用其他模块,这个就不能实现了。因此我需要继续修改把分割出来的两个部分都以参数形式添加入getattr()方法

import home
url=raw_input('url >>>')
"""
将分割出来的列表第一位和第二位分别赋值给
module和func两个参数
"""
module,func=url.split('/')
res=getattr(module,func)
print res()

url >>>home/usr
Traceback (most recent call last):
  File "/Users/cindy/PycharmProjects/untitled/main.py", line 10, in <module>
    res=getattr(module,func)
AttributeError: 'str' object has no attribute 'usr'

看!结果报错了,虽然getattr()方法的第二个参数可以用字符串的方式执行函数。但是第一个参数指定的是要导入的模块名,运行的时候相当于执行了

import home

但是如果我们传入的是一个字符串

就相当于执行的是

import 'home'

这两个命令肯定是不等价的

为了能实现模块的动态导入,我们可以用_import__方法用来将字符串转变成对象

module=__import__('name')

上面这个命令其实就等价于

import home as module

这样子就是实现了模块的动态导入,现在我们再修改一下代码

url=raw_input('url >>>')
"""
将分割出来的列表第一位和第二位分别赋值给
module和func两个参数
"""
module_str,func=url.split('/')
#__import__方法用来将字符串转变成对象
module=__import__(module_str)
res=getattr(module,func)
print res()

url >>>home/usr
home.usr

这样结果就正确了,完美的实现了我们的需求。


那么既然getattr()可以操作函数,那么是否也可以用这个方法来操作类和方法呢?答案是可以的

假设现在fanshe.py文件里有类和方法

class Foo(object):
    staticField = "old boy"
    def __init__(self):
        self.name = 'wupeiqi'
    def func(self):
        return 'func'
    @staticmethod
    def bar():
        return 'bar'
print getattr(Foo, 'staticField')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')

old boy #返回了静态字段的值
<unbound method Foo.func> #因为是普通方法需要通过实例化才有意义
<function bar at 0x1006cc668> #返回了静态类的值所在的内存地址


假设现在home.py文件中不只有上面定义的函数。还包含了一个Foo()类,类里面有方法若干方法

def home():
    return 'home.home'
def etc():
    return 'home.etc'
def usr():
    return 'home.usr'
def var():
    return 'home.var'
def log():
    return 'home.log'
class Foo(object):
    static_name='nba'
    def __init__(self):
        self.name='alex'
    def show(self):
        pass
    @staticmethod
    def static_show():
        pass
    @classmethod
    def class_show(cls):
        pass

这时候如何通过反射返回home模块下的Foo()类里面的static_name字段呢?答案就是通过实例化的方式一层一层的反射,看代码

import home
cls=getattr(home,'Foo') #反射出Foo 相当于cls=Foo
obj=cls() #实例化  相当于obj=Foo()
print cls
#getattr的嵌套找到
s_name=getattr(cls,'static_name')#反射static_name静态字段
print s_name

<class 'home.Foo'>
nba


2、hasattr()

前面我们提到在使用getattr()方法反射的时候,如果模块中不存在指定的函数,那么就会触发attributeError异常。但是调用程序的时候我们有时候并不知道模块中到底定义了哪些函数。这时候我们就需要先使用hasattr()方法检测一下模块中是否有我们要调用的函数,如果有就返回True,没有就返回False。

还是使用home.py作为模块的蓝本,在main.py文件中调用

import home
is_exist=hasattr(home,'usr')
print is_exist

True

这样在调用getattr()方法之前我们就可以先使用hasattr()判断一下指定的函数是否存在,这样避免触发异常

import home
#如果存在指定的函数
is_exist = hasattr(home, temp)
if is_exist:
    #获取函数
    func = getattr(home, temp)
    #执行函数并获得返回值
    ret = func()
    #将函数返回值响应给请求者
    return ret


hasattr()方法在类和面向对象方面的特殊性

我们知道面向对象编程的时候需要通过类来创建对象,那么当对象被创建之后在通过hasattr()的时候,该方法会现在对象里查找是否有指定的方法,如果对象里没有就会跑到创建对象的类里面去寻找方法,类里面有的话也会返回True

class Foo(object):
    static_name='nba'
    def __init__(self):
        self.name='alex'
    def show(self):
        pass
    @staticmethod
    def static_show():
        pass
    @classmethod
    def class_show(cls):
        pass
obj=Foo()
print obj.__dict__
print hasattr(obj,'name')
#对象的特殊性,先在对象的内存中找方法,如果没有就去创建自己的类里面找是否有指定的方法存在
print hasattr(obj,'show')
{'name': 'alex'}
True
True

3、setattr()和delattr()

setattr的作用是设置成员

例如给home模块临时添加一个wgw,调用这个成员就返回'hahaha'。那么就在main.py中输入

setattr(home,'wgw','hahaha')

如果要修改已存在的usr函数返回值就如下输入

setattr(home,'usr','hoho')


delattr的作用就是删除已经存在的成员,例如删除home模块中的usr成员

delattr(home,'usr')


关于反射的总结:反射的所有操作都是在内存中进行的,无论查询、添加、设置和删除。这些操作不会对原始文件造成任何影响。
















本文出自 “霹雳豆包” 博客,谢绝转载!

你可能感兴趣的:(8,day)