How does Python distinguish explicitly passed None as argument in built-ins

ε祈祈猫儿з 提交于 2020-01-04 02:34:26

问题


I experimented with the next code:

>>> f = object()

# It's obvious behavior:
>>> f.foo
Traceback (most recent call last):       
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'

# However, the next one is surprising me!
>>> getattr(f, 'foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'

# And this one returns None as expected:
>>> getattr(f, 'foo', None)

Then I found this pseudo-signature of getattr() in the PyCharm IDE:

def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value

    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass

My question is how does python distinguish this two scenarios of using getattr() (and maybe other functions) internally? And is it possible to do something similar entirely in the client side code?


回答1:


As @scytale said, the pseudo-signature of getattr doesn't quite correspond to its implementations. I've seen attempts to replicate the behaviour in pure Python that look something like this:

class MyObject(object):
    __marker = object()

    def getvalue(key, default=__marker):
        ...
        if key is __marker:
             # no value supplied for default
             ....

In other words, use a marker value that a caller cannot easily supply to check if no value was given as the default rather than None.




回答2:


getattr is implemented in C so how it's done differ a bit from how it's done in python. In C there are a couple of calling conventions, getattr is using what's called METH_VARARG which means that it expects unspecified number positional arguments passed as a tuple, the function then checks that it's either a tuple of length 2 or 3 (via PyArg_UnpackTuple) and acts accordingly (when unpacked the default argument will when omitted be a NULL pointer which is different from any python object).

It's similar of what could be done in python:

def mygetattr(*args):
    if len(args) != 2 and len(args) != 3:
          raise Exception
    try:
        return getattr(args[0], args[1])
    except AttributeError:
        if len(args) == 3:
            return args[2]
        raise

But normally in python one would actually explicitely state the parameters that's mandatory and then use *args to handle the optional parameters (ie def mygetattr(obj, key, *args):...)




回答3:


getattr is a built-in - therefore it is implemented in C - the pseudo-signature is not a precise guide to how it works.




回答4:


I would go in the same direction as @skyking but also allow using named arguments like this:

def getattr(object, name, *args, **kwargs):
    if len(args) == 1:
        # third positional argument is used as default
        default = args[1]
    elif 'default' in kwargs and len(kwargs) == 1:
        # provided as named argument
        default = kwargs['default']
    elif len(kwargs) > 0 or len(args) > 0:
        # unknown arguments
    else:
        # no 'default' was supplied

In the above code, you can insert the processing code and exceptions to be raised wherever it is appropriate!




回答5:


getattr docs says

getattr(object, name[, default])

Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised.

If you want to implement same try this

def mygetattr(obj, attr, default):
    ret_val = defaule
    try:
        ret_val = getattr(obj, attr)
    except AttributeError:
        pass

    return ret_val


来源:https://stackoverflow.com/questions/32499863/how-does-python-distinguish-explicitly-passed-none-as-argument-in-built-ins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!