V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
xinhangliu
V2EX  ›  Python

问几个关于 __getattr__() 的问题,求解答

  •  
  •   xinhangliu · 2017-08-17 09:30:51 +08:00 · 1562 次点击
    这是一个创建于 2659 天前的主题,其中的信息可能已经有所发展或是发生改变。

    初学 Python,最近在看这个项目 zhihu-py3

    基本情况

    • 定义了一个 ZhihuClient 类,主要实现登录相关的操作,同时创建一个网络会话( requests.session );
    • 定义了一个 Author(url, session) 类,传入某作者的知乎域名,传入一个 session,返回 Author 对象,比如 Author.name 可以获取用户名

    目标

    实现这样子调用:

    client = ZhihuClient()
    client.author  # 这是一个 Author 对象
    

    实现方法(我的第一反应)

    我只能想到的是,在 ZhihuClient 里定义一个 author 方法,用来创建并返回 Author 对象

    def author(url, session=self.session):
        author = Author(url, session)
        return author
    

    试了一下是能实现的。但是除了 Author 之外还有好多的类,如果一个一个定义过去那得多麻烦。所以看作者的实现方法

    实现方法(作者的)

    ZhihuClient 里定义 __getattr__(),实现动态调用。下面是作者的源码

    def __getattr__(self, item: str):
        """本函数用于获取各种类,如 `Answer` `Question` 等.
    
        :支持的形式有:
            1. client.answer()
            2. client.author()
            3. client.collection()
            4. client.column()
            5. client.post()
            6. client.question()
            7. client.topic()
    
            参数均为对应页面的 url,返回对应的类的实例。
        """
        def getter(url):
            return getattr(module, item.capitalize())(url, session=self._session)
        attr_list = ['answer', 'author', 'collection',
                     'column', 'post', 'question', 'topic']
        if item.lower() in attr_list:
            module = importlib.import_module('.'+item.lower(), 'zhihu')
            return getter
    

    我的疑问

    1. 我的方法有没有啥问题?(除了麻烦以外)
    2. 作者的方法是不是比较常见的方法?如果不是,希望知道解决这类问题最常见的方法
    3. 作者方法里定义的 getter(url) 方法是用来传递参数的。我尝试将它去掉,直接 return getattr(module, item.capitalize())(url, session=self._session),但是这样子就会报错:参数 url 未定义。我总感觉定义一个 getter 有点多余(没有任何讽刺作者的意思),有没有更好的解决办法?(如何在 __getattr__() 里传递参数)
    4 条回复    2017-08-17 10:55:37 +08:00
    Morriaty
        1
    Morriaty  
       2017-08-17 09:52:29 +08:00   ❤️ 1
    1、没问题,稍微有个小瑕疵
    def author(self, url):
    author = Author(url, self.session)
    return author

    2、如果是我自己写的话,我也会写类似的方法
    3、该函数返回的是类,而不是实例。
    nullcc
        2
    nullcc  
       2017-08-17 09:56:09 +08:00   ❤️ 1
    当用户试图访问一个根本不存在(或者暂时不存在)的属性时,你可以通过__getattr__魔法方法来定义类的行为
    xinhangliu
        3
    xinhangliu  
    OP
       2017-08-17 09:59:39 +08:00
    @Morriaty 感谢纠正
    keakon
        4
    keakon  
       2017-08-17 10:55:37 +08:00   ❤️ 1
    1. 代码不可复用。
    2. 常见,但由于效率较低,所以不到万不得已一般也不用。Ruby 的话倒是经常这么干。除此以外还可以用 descriptor 机制(__get__),代码量会多一些,但是 IDE 可以识别这些属性,避免出现警告。
    3. url 是传给 Answer(self, url) 的,这是你要暴露出去的参数,干掉就没法初始化了。你实在不想给 getter 起名,可以 return lambda url: getattr(module, item.capitalize())(url, session=self._session)。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3665 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 00:12 · PVG 08:12 · LAX 16:12 · JFK 19:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.