I was learning the python. And when comes to the collection module in official library, I found a code snipet of the NamedTuple like:
for i, name in enumerate(field_names):
template += " %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i)
And it's one part of the code that generated by the NamedTuple. The code generated is listed below:
name = property(itemgetter(0), doc='Alias for field number 0')
age = property(itemgetter(1), doc='Alias for field number 1')
And here is my question:
Itemgetter(0) is a function which needs an object as arguments. But property will not pass any arguments to the itemgetter. So how does this work?
Thank you!
This is the whole code that property is used:
class Person(tuple):
'Person(name, age)'
__slots__ = ()
_fields = ('name', 'age')
def __new__(_cls, name, age):
'Create new instance of Person(name, age)'
print sys._getframe().f_code.co_name
return _tuple.__new__(_cls, (name, age))
@classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Person object from a sequence or iterable'
print sys._getframe().f_code.co_name
result = new(cls, iterable)
if len(result) != 2:
raise TypeError('Expected 2 arguments, got %d' % len(result))
return result
def __repr__(self):
'Return a nicely formatted representation string'
print sys._getframe().f_code.co_name
return 'Person(name=%r, age=%r)' % self
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
print sys._getframe().f_code.co_name
return OrderedDict(zip(self._fields, self))
def _replace(_self, **kwds):
'Return a new Person object replacing specified fields with new values'
print sys._getframe().f_code.co_name
result = _self._make(map(kwds.pop, ('name', 'age'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
print sys._getframe().f_code.co_name
return tuple(self)
name = property(itemgetter(0), doc='Alias for field number 0')
age = property(itemgetter(1), doc='Alias for field number 1')
itemgetter
is not a function, it's a class whose instances are callable (cf the FineManual). The property
instance will call it with the current object as argument (that's what properties are for).
Let's summarize... assuming this:
point = tuple(1, 2)
getx = itemgetter(0)
passing point
to getx()
will return point[0]
(actually, point.__getitem__[0]
for which point[0]
is syntactic sugar)
Now if we subclass tuple
and add a property
:
class Point(tuple):
@property
def x(self):
return self[0]
The @decorator syntax is actually syntactic sugar for :
class Point(tuple):
def x(self):
return self[0]
x = property(fget=x)
so the function x
becomes the fget
attribute of the property
instance, and the name x
in the class statement's namespace is rebound to this property
instance.
Now let's create a Point
instance:
point = Point(1, 2)
then when evaluating point.x
, the attributes lookup rules will find the "x" property
object on Point
(actually on point.__class__
), notice that it has a __get__()
method, and according to the descriptor protocol will return the result of Point.x.__get__(point, Point.__class__)
. Since property.__get__(obj, cls)
is mainly implemented as return self.fget(obj)
, this will return the result of the x
function called with point
as self
param. IOW:
point.x
is equivalent to
Point.x.__get__(point, point.__class__)
which is equivalent to
Point.x.fget(point)
which is equivalent to (NB : here 'x' refers the x
function that has been passed as fget
argument to property
, not to Point.x
x(point)
which is equivalent to
point[0]
And since itemgetter(0)(point)
is equivalent to point[0]
, one can see how x = property(itemgetter(0))
works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.