__getattr__动态获取接口

# -*- coding:utf-8 -*-
#在看廖雪峰的python3.5教学时,看到面向对象高级编程_定义类
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319098638265527beb24f7840aa97de564ccc7f20f6000
百度了很久没有找到想要的答案,想了很久,总结了一点点心得,写的不好,希望大家指正
途中发现好多问题,包括前面没有理解的,停留了好久
##################################
#正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类:


class Student(object):

    def __init__(self):
        self.name = 'Michael'


#调用name属性,没问题,但是,调用不存在的score属性,就有问题了:
#>>> s = Student()
#>>> print(s.name)
#Michael
#>>> print(s.score)
#Traceback (most recent call last):
#  ...
#AttributeError: 'Student' object has no attribute 'score'

#错误信息很清楚地告诉我们,没有找到score这个attribute。
#要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:


class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99


#当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

#>>> s = Student()
#>>> s.name
#'Michael'
#>>> s.score
#99

#返回函数也是完全可以的:


class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25


#只是调用方式要变为:
#>>> s.age()
#25


#注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

#此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:


class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

		
#这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

#这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

#举个例子:

#现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

#http://api.server/user/friends
#http://api.server/user/timeline/list
#如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

#利用完全动态的__getattr__,我们可以写出一个链式调用:


class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__


#测试结果
#>>> Chain().status.user.timeline.list     
#'/status/user/timeline/list'

#==========================================
#这里想了很久;查看一个类
#创建了一个空的类,再创建一个实例实例


class Student(object):
    pass

#bart = Student()
#变量bart指向的就是一个Student的实例

#再看刚刚那个例子,Chain()表示类自己(本身);
#Chain().status表示的类Chain()的status属性,此时函数就去寻找Chain的status属性
#但发现没有此属性,这时特殊函数__getattr__起作用了,它帮我们创建属性status属性
#根据定义的方法将新的属性前加一个'/'

#初始化使用了默认参数path为空
#def __init__(self, path=''):

#连贯起来就是
#1.先定义类Chain()的一个属性path,切默认值为'',为空
#2.定义方法,特殊函数__getattr__遇到未定义的属性默认返回空,不报错
#由于定义了方法,所以返回return Chain('%s/%s' % (self._path, path))
#即将未定义的额属性以'/'为分隔符输出
#3.方法def __str__(self):的作用是打印字符串

#当然还有细节没有搞清楚
#1.class中的各方法参数是表示同一个值吗?
#即三个方法中的self._path相关么?
posted @ 2017-11-13 22:56  陈浩然201  阅读(423)  评论(0编辑  收藏  举报