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
sdjl
V2EX  ›  Python

Python 中如何实现 a.xxx 返回 a 本身?

  •  
  •   sdjl · 2017-08-26 19:11:07 +08:00 · 5001 次点击
    这是一个创建于 2653 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我想要实现一个功能,有 class A,a = A(),a == None 返回 True,同时 a.xxx (其中 xxx 可以随表写)返回 a 本身。

    下面说说我的目的,我经常有这样的代码:

    if a and a.b and a.b.c and a.b.c.d:
    # do something

    我觉得这样写太丑了,所以希望可以直接写成:

    if a.b.c.d:
    # do something

    目前我写了一个代码,如下:



    运行此代码时抛出异常:
    Traceback (most recent call last):
    File "test.py", line 19, in <module>
    print a.sdjl
    RuntimeError: maximum recursion depth exceeded
    29 条回复    2017-08-27 15:10:44 +08:00
    sdjl
        1
    sdjl  
    OP
       2017-08-26 19:19:16 +08:00
    前 20 个回复必感谢!
    ivechan
        2
    ivechan  
       2017-08-26 19:22:53 +08:00   ❤️ 1
    你直接把代码块 a.b.c.d try 一下不就行了。
    kotlin 倒是有处理你这种问题的解决方法,Python 我还没见过。。
    sdjl
        3
    sdjl  
    OP
       2017-08-26 19:25:21 +08:00
    @ivechan 那不是一样丑么
    xkoing
        4
    xkoing  
       2017-08-26 19:28:34 +08:00   ❤️ 1
    honmaple
        5
    honmaple  
       2017-08-26 19:30:01 +08:00   ❤️ 1
    没看懂什么需求, a 必定不为 None 啊,或者只要__getattr__
    WangYanjie
        6
    WangYanjie  
       2017-08-26 19:45:47 +08:00   ❤️ 1
    ```
    >>> class A(object):
    ... @property
    ... def xxx(self):
    ... return self
    ...
    >>> a = A()
    >>> a
    <__main__.A object at 0x10e8809d0>
    >>> a.xxx
    <__main__.A object at 0x10e8809d0>
    ```
    cheetah
        7
    cheetah  
       2017-08-26 19:47:49 +08:00   ❤️ 3
    我这样回复你也感谢?
    Cooky
        8
    Cooky  
       2017-08-26 19:56:59 +08:00 via Android   ❤️ 1
    try 最省事,最明白
    zwgmlr3
        9
    zwgmlr3  
       2017-08-26 20:07:16 +08:00 via Android   ❤️ 1
    if xxx(a,'a.b.c.d'): pass
    zwgmlr3
        10
    zwgmlr3  
       2017-08-26 20:23:44 +08:00   ❤️ 1
    ```
    def hasattrs(obj, attrs):
    attrs = attrs.split('.')
    objs = attrs[0]
    for attr in attrs[1:]:
    if hasattr(obj, attr):
    obj = eval('{}.{}'.format(objs, attr))
    objs += '.{}'.format(attr)
    else:
    return False
    return obj

    class C():
    d = 233


    class B():
    c = C()


    class A():
    b = B()


    a = A()
    print(hasattrs(a, 'a.b.c.d'))
    print(hasattrs(a, 'a.b.e.d'))
    ```
    大概这样
    zwgmlr3
        11
    zwgmlr3  
       2017-08-26 20:25:37 +08:00   ❤️ 2
    hahastudio
        12
    hahastudio  
       2017-08-26 21:09:24 +08:00   ❤️ 2
    好问题,这个语法类似于 C# null-conditional operator https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
    if a?.b?.c?.d:
    PEP 505 提出过这个想法 https://www.python.org/dev/peps/pep-0505/

    如果你只是想要 class A 下面一层的话,那就是 getattr
    但如果你想要语法层面的东西的话,还是一串 and 吧
    VShawn
        13
    VShawn  
       2017-08-26 21:19:24 +08:00 via Android   ❤️ 1
    第一次见到这么奇怪的需求。。。这个有什么用吗?
    sdjl
        14
    sdjl  
    OP
       2017-08-27 00:05:37 +08:00
    @VShawn

    很有用,举个例子,假设有表 A,其中有一个字段名为 Bid,这个字段保存了表 B 的主键,用某种方法得到表 A 的某个数据 a 后,就可以用 a.b 直接得到表 B 的数据(其中 b 的 id 等于 a.Bid )

    同理,可以 a.b.c.d 这样得到多个表的关联数据,例如:product.shop.mall.address

    我的程序中大量使用了这样的代码,这样我就不用去操作数据库了,例如我有列表 products,现在需要得每个商品的商家列表,可以这样写:

    shops = [p.shop fpr p in products]

    注意,p.shop 这一步其实自动查询的表 Shop,如果我不调用 p.shop,就没有查表操作
    sdjl
        15
    sdjl  
    OP
       2017-08-27 00:11:54 +08:00
    这样,如果我有一个 product,我需要判断这个商品所在商家所在商场是否有地址信息,我就可以直接写成:

    if product.shop.mall.address:
    # do something

    如果在其中某一步发现数据不存在,例如 shop 其实不存在,那么 shop 得到的其实就是我问题中提出的 class A 的实例 a。

    此时 product.shop == None,且同时 product.shop.mall.address == product.shop.mall == producj.shop == None
    PythonAnswer
        16
    PythonAnswer  
       2017-08-27 00:14:19 +08:00 via Android   ❤️ 1
    前排帮顶 蹭个感谢
    ToBeHacker
        17
    ToBeHacker  
       2017-08-27 00:53:36 +08:00   ❤️ 1
    直接用,try 一下就行了。 改函数的话更麻烦,要使用异常检查的机制啊。
    二楼正解
    byx
        18
    byx  
       2017-08-27 01:09:17 +08:00   ❤️ 1
    yangff
        19
    yangff  
       2017-08-27 01:25:45 +08:00   ❤️ 1
    >>> class A:
    ... def __getattr__(self, key):
    ... return self
    ... def __repr__(self):
    ... return 'None'
    ...
    simadad
        20
    simadad  
       2017-08-27 01:28:11 +08:00 via Android   ❤️ 1
    这个有需求很像 django 里的 qureyset,可以借鉴下 django 的源码
    yangff
        21
    yangff  
       2017-08-27 01:29:55 +08:00   ❤️ 1
    PS: 更合理的做法是

    >>> class A:
    ... def __getattr__(self, key):
    ... if (key.startswith('_')):
    ... raise AttributeError
    ... return self
    oott123
        22
    oott123  
       2017-08-27 01:32:10 +08:00 via Android
    你有没有想过,a.anything 都返回 a 的话,你就拿不到 a 上任何东西了…
    比如 product.shop 返回 product,那么 product.shop.mall 也返回 product,product.shop.mall.address 也返回 product,这…
    yangff
        23
    yangff  
       2017-08-27 01:34:23 +08:00   ❤️ 2
    @oott123 他的意思是制造一个 blackhole 来使得 none.xxx 不崩溃且 blackhole==none 成立
    guyskk
        24
    guyskk  
       2017-08-27 10:34:22 +08:00 via Android   ❤️ 1
    你这是在挖坑啊,到处是 N+1 查询,按你的描述 product.shop.mall.address 就要查 2 次数据库,多写几行就几十次了
    explist
        25
    explist  
       2017-08-27 11:55:30 +08:00   ❤️ 1
    描述器看看?
    sdjl
        26
    sdjl  
    OP
       2017-08-27 14:40:09 +08:00
    @yangff 非常感谢,此方法可行! 能解释一下为什么这样可以么?
    sdjl
        27
    sdjl  
    OP
       2017-08-27 14:41:19 +08:00
    @guyskk 宁可多费机器一分,不费程序员一秒,体育老师没教过么~
    yangff
        28
    yangff  
       2017-08-27 15:07:25 +08:00   ❤️ 2
    @sdjl 你 print 的时候,python 会尝试把 object 搞成字符串,这会导致 python 尝试调用__repr__或者__str__
    正常的 class A 会导致 AttributeError,于是 python 就知道不能这么操作。
    你的 class A 则是这样的
    a.__repr__ = a.__str__ = a.__call__ = a
    于是,首先 python 获得 a.__repr__,然后尝试调用 a.__repr__(a),因为 a 不是函数,但是 a.__call__也就是 a.__repr__.__call__存在,所以会尝试执行 a.__repr__.__call__(),因为 a.__repr__.__call__ = a.__call__ = a, 所以 a.__repr__.__call__()又会去找 a.__repr__.__call__.__call__然后执行它……
    于是就无限递归啦
    sdjl
        29
    sdjl  
    OP
       2017-08-27 15:10:44 +08:00
    @yangff 明白了,非常感谢!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2606 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 06:58 · PVG 14:58 · LAX 22:58 · JFK 01:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.