V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Cheez
V2EX  ›  问与答

Python 如何重载?

  •  
  •   Cheez · 2018-07-21 15:00:44 +08:00 · 2871 次点击
    这是一个创建于 2321 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我想要这样的效果:

    ans.vote --> 返回数目 ans.vote() --> 赞同答案

    可是具体操作时却这样: TypeError: 'NoneType' object is not callable

    第 1 条附言  ·  2018-07-21 15:35:35 +08:00
    class Ans(object): 
    
        def __init__(self ):
            pass
    
        def vote(self):
            print('赞同他')
    
        @property
        def vote(self):
            print('获取赞同'+str(self._vote))
             
    
    Ans().vote
    >> '获取赞同9'
    Ans().vote(2)
    >>TypeError: 'NoneType' object is not callable
    

    我又试了一下:

    class aaa(object): 
        def __init__(self):
            pass
    
        def __get__(self, instance, owner):
            print('get')
    
        def __call__(self, *arg):
            print('call')
    
    
    class ccc(object):
        """docstring for ccc"""
        a = aaa() 
    
        def __init__(self):
            pass
     
    aaa()()
    >> get
    ccc().a
    >> get
    ccc().a()
    >> TypeError: 'NoneType' object is not callable
    
    第 2 条附言  ·  2018-07-21 19:34:30 +08:00
    def intcan(call):
        def allcan_func(f):
    
            class allcan_class(int):
                def __call__(self, instance, *args, **kwargs):
    
                    return call(instance)
    
                def __get__(self, instance, owner):
                    return allcan_class(f(instance))
            return allcan_class()
        return allcan_func
    

    最后用这个装饰器实现了ans.vote,ans.vote()的功能

    但是有个问题,调用ans.vote的时候会自动调用ans.vote值的相关代码,不知道怎么解决

    第 3 条附言  ·  2018-07-22 11:16:40 +08:00
    因为问题始终无法解决,最后换成了这种写法:
    ```
    def vote(self):
    print('赞同他')
    self.vote.__dict__['count'] = self._vote
    return self

    ```
    调用的时候:
    ```
    print('方法')
    print(Article('37208344').vote())
    print('属性')
    print(Article('37208344').vote.count)
    ```
    25 条回复    2018-07-22 13:14:35 +08:00
    hlwjia
        1
    hlwjia  
       2018-07-21 15:21:55 +08:00
    你的 ans 是 None ?
    mimzy
        2
    mimzy  
       2018-07-21 15:26:44 +08:00
    Python 没有重载(可能不严格,请指正),因为有可变参数和默认参数。

    你的 ans 或者实例中的某些东西是 None
    Cheez
        3
    Cheez  
    OP
       2018-07-21 15:36:08 +08:00
    不是 None,具体见附言.
    u2386
        4
    u2386  
       2018-07-21 15:39:13 +08:00   ❤️ 1
    Trim21
        5
    Trim21  
       2018-07-21 15:41:52 +08:00   ❤️ 1
    class vote(int):
    def __call__(self, *args, **kwargs):
    print('vote')

    pass


    class a(object):
    vote = vote(1)


    ans = a()
    print(ans.vote)
    ans.vote()
    u2386
        6
    u2386  
       2018-07-21 15:41:52 +08:00   ❤️ 2
    附言里加了 property 的 vote 覆盖了上一个 vote 方法,并且只有 print,没有 return,所以调用这个方法返回是个 None。
    Cheez
        7
    Cheez  
    OP
       2018-07-21 17:15:50 +08:00
    最后这样好了

    ```

    def intcan(call):
    def allcan_func(f):

    class vote(int):
    def __call__(self, *args, **kwargs):

    return call()

    def __get__(self, instance, owner):
    return vote(f())
    return vote()
    return allcan_func
    ```

    就是很不优雅(笑哭
    @u2386 #6
    @Trim21
    ipwx
        8
    ipwx  
       2018-07-21 17:25:53 +08:00
    强烈反对楼主的做法。
    Cheez
        9
    Cheez  
    OP
       2018-07-21 17:51:19 +08:00 via Android
    @ipwx 怎么了
    yezhiye
        10
    yezhiye  
       2018-07-21 18:25:57 +08:00 via Android
    这样做挺奇怪的,因为可以定义函数 func def func()...然后 a = func,这样就可以通过 a()执行 func。如果名字一样的话就覆盖了。
    Cheez
        11
    Cheez  
    OP
       2018-07-21 19:28:14 +08:00
    @yezhiye #10

    zhihu.vote()
    zhihu.vote

    两个一个赞同,一个获取赞同,这不是很爽嘛
    Cheez
        12
    Cheez  
    OP
       2018-07-21 19:35:11 +08:00
    def intcan(call):
    def allcan_func(f):

    class allcan_class(int):
    def __call__(self, instance, *args, **kwargs):

    return call(instance)

    def __get__(self, instance, owner):
    return allcan_class(f(instance))
    return allcan_class()
    return allcan_func
    最后用这个装饰器实现了 ans.vote,ans.vote()的功能

    但是有个问题,调用 ans.vote 的时候会自动调用 ans.vote 值的相关代码,不知道怎么解决
    @Trim21 #5
    @yezhiye
    @ipwx #8
    @u2386 #4
    Trim21
        13
    Trim21  
       2018-07-21 19:58:45 +08:00
    @Cheez #12 这里没必要重载__get__吧...
    Cheez
        14
    Cheez  
    OP
       2018-07-21 20:02:38 +08:00
    @Trim21 #13

    def vote_call(self):
    print('赞同他')

    @varCan(vote_call)
    def vote(self):
    print('获取赞同'+str(self._vote))
    return self._vote

    get 的时候也是要调用一个函数计算得到值的
    Trim21
        15
    Trim21  
       2018-07-21 20:07:00 +08:00
    @Cheez #14 为什么不直接用两个类呢...
    这种写法没感觉相比用两个类有什么优势...
    方便自定义__call__?
    Cheez
        16
    Cheez  
    OP
       2018-07-21 20:12:26 +08:00
    @Trim21 #15
    调用的时候比较方便一点 TAT
    mingyun
        17
    mingyun  
       2018-07-21 22:43:01 +08:00
    函数里定义类 python 有点怪
    gnijuohz
        18
    gnijuohz  
       2018-07-21 23:19:44 +08:00 via iPhone
    不太明白为什么非要都用 vote 这词

    从语法上讲名词( property )用 votes 这个复数形式更恰当
    然后进行投票用 vote
    wangyongbo
        19
    wangyongbo  
       2018-07-21 23:49:33 +08:00   ❤️ 1
    这两天升级 django , 从 1.8 升级到 支持 python2.7 的最后一个版本 1.11.
    发现
    "Using user.is_authenticated() and user.is_anonymous() as a method "
    "is deprecated. Remove the parentheses to use it as an attribute.",
    之前的使用方法:user.is_authenticated()
    现在的使用方法:user.is_authenticated

    我看了一下 django 的实现方法

    ```
    class User():

    @property
    def is_authenticated(self):
    return CallableFalse

    ```
    首先用 property 把它变成了一个属性,但是返回的不是一个 bool, 是一个有__call__ 的对象

    CallableFalse = CallableBool(False)


    ```
    class CallableBool:
    """
    An boolean-like object that is also callable for backwards compatibility.
    """
    do_not_call_in_templates = True

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

    def __bool__(self):
    return self.value

    def __call__(self):
    warnings.warn(
    "Using user.is_authenticated() and user.is_anonymous() as a method "
    "is deprecated. Remove the parentheses to use it as an attribute.",
    RemovedInDjango20Warning, stacklevel=2
    )
    return self.value

    def __nonzero__(self): # Python 2 compatibility
    return self.value

    def __repr__(self):
    return 'CallableBool(%r)' % self.value

    def __eq__(self, other):
    return self.value == other

    def __ne__(self, other):
    return self.value != other

    def __or__(self, other):
    return bool(self.value or other)

    def __hash__(self):
    return hash(self.value)

    ```


    你觉得这种实现方式 怎么样?

    出了 这种需要兼容的代码, 再也没有见过 类似的代码了。
    ipwx
        21
    ipwx  
       2018-07-22 00:23:08 +08:00 via iPhone
    @Cheez 如果我想拿 vote 这个函数对象怎么办?
    Cheez
        22
    Cheez  
    OP
       2018-07-22 02:39:05 +08:00 via Android
    @ipwx 拿来好像也没什么用...
    Cheez
        23
    Cheez  
    OP
       2018-07-22 11:17:14 +08:00
    因为问题始终无法解决,最后换成了这种写法:
    ```
    def vote(self):
    print('赞同他')
    self.vote.__dict__['count'] = self._vote
    return self

    ```
    调用的时候:
    ```
    print('方法')
    print(Article('37208344').vote())
    print('属性')
    print(Article('37208344').vote.count)
    ```
    @ipwx #21
    @wangyongbo #19
    @Trim21 #15
    laike9m
        24
    laike9m  
       2018-07-22 12:01:54 +08:00 via Android
    这接口设计也是醉了,弄个叫 vote_count 的 property 不好么
    Trim21
        25
    Trim21  
       2018-07-22 13:14:35 +08:00 via Android
    @Cheez …那为什么不写两个 class 呢…满足你最一开始的要求,还比现在这样好
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5542 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 07:12 · PVG 15:12 · LAX 23:12 · JFK 02:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.