How to access args as a list?

Discussion in 'Python' started by kj, Apr 3, 2010.

  1. kj

    kj Guest

    Suppose I have a function with the following signature:

    def spam(x, y, z):
    # etc.

    Is there a way to refer, within the function, to all its arguments
    as a single list? (I.e. I'm looking for Python's equivalent of
    Perl's @_ variable.)

    I'm aware of locals(), but I want to preserve the order in which
    the arguments appear in the signature.

    My immediate aim is to set up a simple class that will allow me to
    iterate over the arguments passed to the constructor (plus let me
    refer to these individual arguments by their names using an
    instance.attribute syntax, as usual).

    The best I have managed looks like this:

    class _Spam(object):
    def __init__(self, x, y, z):
    self.__dict__ = OrderedDict(())
    for p in inspect.getargspec(_Spam.__init__).args[1:]:
    self.__dict__[p] = locals()[p]

    def __iter__(self):
    return iter(self.__dict__.values())


    but rolling out inspect.getargspec for this sort of thing looks to
    me like overkill. Is there a more basic approach?

    P.S. this is just an example; the function I want to implement has
    more parameters in its signature, with longer, more informative
    names.
    kj, Apr 3, 2010
    #1
    1. Advertising

  2. kj

    Tim Chase Guest

    kj wrote:
    >
    >
    > Suppose I have a function with the following signature:
    >
    > def spam(x, y, z):
    > # etc.
    >
    > Is there a way to refer, within the function, to all its arguments
    > as a single list? (I.e. I'm looking for Python's equivalent of
    > Perl's @_ variable.)


    It sounds like you want the "*" operator for arguments:

    def foo(*args):
    print "Args:", repr(args)
    print " Type:", type(args)
    for i, arg in enumerate(args):
    print " Arg #%i = %s" % (i, arg)
    foo(1)
    foo("abc", "def", 42)
    # pass a list as args
    lst = [1,2,3]
    foo(*lst)

    There's also a keyword form using "**":

    def foo(*args, **kwargs):
    print "Args:", repr(args)
    print " Type:", type(args)
    print "Keyword Args:", repr(kwargs)
    print " Type:", type(kwargs)
    for i, arg in enumerate(args):
    print " Arg #%i = %s" % (i+1, arg)
    for i, (k, v) in enumerate(kwargs.items()):
    print " kwarg #%i %r = %s" % (i+1, k, v)
    foo(1, "hello", something="Whatever", baz=42)
    # pass a list and a dict:
    lst = [1,2,3]
    dct = {100:10, 200:11}
    foo(*lst, **dct)

    Both prevent introspection, so if you want the auto-generated
    help to populate with named arguments, you'd have to use the
    inspection method you're currently using. But generally, if you
    want to be able to treat the args as lists/tuples/dicts, you
    don't care about the names given.

    -tim
    Tim Chase, Apr 4, 2010
    #2
    1. Advertising

  3. kj

    MRAB Guest

    kj wrote:
    >
    >
    > Suppose I have a function with the following signature:
    >
    > def spam(x, y, z):
    > # etc.
    >
    > Is there a way to refer, within the function, to all its arguments
    > as a single list? (I.e. I'm looking for Python's equivalent of
    > Perl's @_ variable.)
    >
    > I'm aware of locals(), but I want to preserve the order in which
    > the arguments appear in the signature.
    >
    > My immediate aim is to set up a simple class that will allow me to
    > iterate over the arguments passed to the constructor (plus let me
    > refer to these individual arguments by their names using an
    > instance.attribute syntax, as usual).
    >
    > The best I have managed looks like this:
    >
    > class _Spam(object):
    > def __init__(self, x, y, z):
    > self.__dict__ = OrderedDict(())
    > for p in inspect.getargspec(_Spam.__init__).args[1:]:
    > self.__dict__[p] = locals()[p]
    >
    > def __iter__(self):
    > return iter(self.__dict__.values())
    >
    >
    > but rolling out inspect.getargspec for this sort of thing looks to
    > me like overkill. Is there a more basic approach?
    >
    > P.S. this is just an example; the function I want to implement has
    > more parameters in its signature, with longer, more informative
    > names.
    >

    I think the closest approach to what you're asking is to capture the
    arguments as a list and then bind them to local names:

    def spam(*args):
    x, y, z = args
    # etc.
    MRAB, Apr 4, 2010
    #3
  4. On Sat, Apr 3, 2010 at 6:28 PM, kj <> wrote:
    > Is there a way to refer, within the function, to all its arguments
    > as a single list?  (I.e. I'm looking for Python's equivalent of
    > Perl's @_ variable.)
    >


    def spam(*args, **kwargs):
    print args
    print kwargs

    class Spam:
    def __init__(self, *args, **kwargs):
    print args
    print kwargs

    That's what are you looking for?

    Regards,

    ~Rolando
    Rolando Espinoza La Fuente, Apr 4, 2010
    #4
  5. kj

    kj Guest

    In <hp8h73$k17$> kj <> writes:

    >Suppose I have a function with the following signature:


    >def spam(x, y, z):
    > # etc.


    >Is there a way to refer, within the function, to all its arguments
    >as a single list? (I.e. I'm looking for Python's equivalent of
    >Perl's @_ variable.)


    >I'm aware of locals(), but I want to preserve the order in which
    >the arguments appear in the signature.


    >My immediate aim is to set up a simple class that will allow me to
    >iterate over the arguments passed to the constructor (plus letS me

    ^^^^^^^^^^^^
    >refer to these individual arguments by their names using an

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >instance.attribute syntax, as usual).

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    The underlined portion explains why __init__(self, *args) fails to
    fit the bill.

    >P.S. this is just an example; the function I want to implement has
    >more parameters in its signature, with longer, more informative
    >names.


    Andreas, perhaps this paragraph explains why I find your solution
    unappealing: it requires typing the same thing over and over,
    which increases the chances of bugs. That's the reason I avoid
    such repetitiveness, not laziness, as you so were so quick to accuse
    me of.

    ~K
    kj, Apr 4, 2010
    #5
  6. kj

    kj Guest

    In <hp8kc9$dg7$> kj <> writes:

    >In <hp8h73$k17$> kj <> writes:


    >>Suppose I have a function with the following signature:


    >>def spam(x, y, z):
    >> # etc.


    >>Is there a way to refer, within the function, to all its arguments
    >>as a single list? (I.e. I'm looking for Python's equivalent of
    >>Perl's @_ variable.)


    >>I'm aware of locals(), but I want to preserve the order in which
    >>the arguments appear in the signature.


    >>My immediate aim is to set up a simple class that will allow me to
    >>iterate over the arguments passed to the constructor (plus letS me

    > ^^^^^^^^^^^^
    >>refer to these individual arguments by their names using an

    > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >>instance.attribute syntax, as usual).

    > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


    >The underlined portion explains why __init__(self, *args) fails to
    >fit the bill.


    The minute I hit "send" I realized that this is wrong. Sorry.

    Thanks.
    kj, Apr 4, 2010
    #6
  7. On Sat, 3 Apr 2010 22:58:43 +0000 (UTC) kj <> wrote:

    > The best I have managed looks like this:
    >
    > class _Spam(object):
    > def __init__(self, x, y, z):
    > self.__dict__ = OrderedDict(())
    > for p in inspect.getargspec(_Spam.__init__).args[1:]:
    > self.__dict__[p] = locals()[p]
    >
    > def __iter__(self):
    > return iter(self.__dict__.values())


    Looks like you're trying to be lazy by doing extra hard work. Anything
    wrong with this (?):

    class Spam(object):
    def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z
    self.arguments = (x, y, z)

    def __iter__(self):
    return iter(self.arguments) # iter(...) kinda optional

    With what little I know about your use case, that's how I would do it.

    /W


    --
    INVALID? DE!
    Andreas Waldenburger, Apr 4, 2010
    #7
  8. On 2010-04-03, kj <> wrote:
    > In <hp8h73$k17$> kj <> writes:
    >
    >>Suppose I have a function with the following signature:

    >
    >>def spam(x, y, z):
    >> # etc.

    >
    >>Is there a way to refer, within the function, to all its arguments
    >>as a single list? (I.e. I'm looking for Python's equivalent of
    >>Perl's @_ variable.)

    >
    >>I'm aware of locals(), but I want to preserve the order in which
    >>the arguments appear in the signature.

    >
    >>My immediate aim is to set up a simple class that will allow me to
    >>iterate over the arguments passed to the constructor (plus letS me

    > ^^^^^^^^^^^^
    >>refer to these individual arguments by their names using an

    > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >>instance.attribute syntax, as usual).

    > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    >
    > The underlined portion explains why __init__(self, *args) fails to
    > fit the bill.


    then add the line below:

    x,y,z = *args

    --
    Grant
    Grant Edwards, Apr 4, 2010
    #8
  9. On Sat, 3 Apr 2010 23:52:42 +0000 (UTC) kj <> wrote:

    > In <hp8h73$k17$> kj <> writes:
    >
    > [snip]
    > >P.S. this is just an example; the function I want to implement has
    > >more parameters in its signature, with longer, more informative
    > >names.

    >
    > Andreas, perhaps this paragraph explains why I find your solution
    > unappealing: it requires typing the same thing over and over,
    > which increases the chances of bugs. That's the reason I avoid
    > such repetitiveness, not laziness, as you so were so quick to accuse
    > me of.
    >

    First up: I am sorry, I did not mean to offend. I apologize if I did.

    But I stand by my point: If this class is the only instance of this
    pattern, you'll only do this once. I see no reason to automate
    something that will run only once anyway.

    From your own reply to the above it seems that the *args-notation
    solves your problem (if I understand correctly). Since I'm patronizing
    by nature, I'd like to weigh in on this: The approach you describe
    seems like a Perl-ism[1]. *args is meant for variable argument lists.
    Using it as a shortcut for "many formal parameters" is misuse of that
    feature (not the worst kind, but not very good either). In addition to
    creating the impression that the function takes a variable number of
    arguments, it breaks introspection/self-documentation. Also, "many
    formal parameters" can be a signal to rethink the approach altogether.
    I feel that if a function has more than 6 or so parameters, I'm missing
    an opportunity to simplify my code.

    All of the above is philosophy, of course, and mine to boot, so feel
    free to ignore my rambling. But maybe you can elaborate a bit on why
    you need names *and* sequence of your parameters preserved and why
    there need to be so many of them. Perhaps a fresh perspective on this
    will yield a less problematic approach.

    Then again, maybe I have wasted your time enough already. ;)

    /W

    --
    INVALID? DE!
    Andreas Waldenburger, Apr 4, 2010
    #9
  10. On Sat, 03 Apr 2010 22:58:43 +0000, kj wrote:

    > Suppose I have a function with the following signature:
    >
    > def spam(x, y, z):
    > # etc.
    >
    > Is there a way to refer, within the function, to all its arguments as a
    > single list? (I.e. I'm looking for Python's equivalent of Perl's @_
    > variable.)


    Does this help?

    >>> def spam(a, b, c=3, d=4):

    .... pass
    ....
    >>> spam.__code__.co_varnames

    ('a', 'b', 'c', 'd')

    The hardest part is having the function know its own name.

    I see that you are already using the inspect module. That almost
    certainly is the correct approach. I'd be surprised if inspect is too
    heavyweight, but if it is, you can pull out the bits you need into your
    own function.


    --
    Steven
    Steven D'Aprano, Apr 4, 2010
    #10
  11. On 4 Apr, 00:58, kj <> wrote:
    > Suppose I have a function with the following signature:
    >
    > def spam(x, y, z):
    >     # etc.
    >
    > Is there a way to refer, within the function, to all its arguments
    > as a single list?  (I.e. I'm looking for Python's equivalent of
    > Perl's @_ variable.)
    >
    > I'm aware of locals(), but I want to preserve the order in which
    > the arguments appear in the signature.
    >
    > My immediate aim is to set up a simple class that will allow me to
    > iterate over the arguments passed to the constructor (plus let me
    > refer to these individual arguments by their names using an
    > instance.attribute syntax, as usual).
    >
    > The best I have managed looks like this:
    >
    > class _Spam(object):
    >     def __init__(self, x, y, z):
    >         self.__dict__ = OrderedDict(())
    >         for p in inspect.getargspec(_Spam.__init__).args[1:]:
    >             self.__dict__[p] = locals()[p]
    >
    >     def __iter__(self):
    >         return iter(self.__dict__.values())
    >
    > but rolling out inspect.getargspec for this sort of thing looks to
    > me like overkill.  Is there a more basic approach?
    >
    > P.S. this is just an example; the function I want to implement has
    > more parameters in its signature, with longer, more informative
    > names.


    Hi, I once tried something to emulate in python the way Scala language
    allows to automatically generate class attributes from constructor
    parameter. I never tried in real code, but see if it fits your bill.
    It uses class decorators though, so only works with python3. Here is
    my code:

    class FieldsDecorator:
    """It adds a generic scala-like constructor to a class.
    You can create as instance as c = MyClass(f1=3, f2=4)
    and have automatically c.f1=3, c.f2=4.
    Only parameter names listed in the decorator are allowed.
    """
    def __init__(self, *names):
    self.names = names

    def __call__(self, cls):
    def constructor(instance, **kwds):
    for n,v in kwds.items():
    if n in self.names:
    setattr(instance, n, v)
    else: raise TypeError("%s is not a valid field" % s )
    setattr(cls, '__init__', constructor )
    return cls


    @FieldsDecorator("uno", "due")
    class Prova:
    pass

    p = Prova(uno=12, due=9)
    print (p.uno, p.due )

    Ciao
    ----
    FB
    Francesco Bochicchio, Apr 4, 2010
    #11
  12. kj

    kj Guest

    In <4bb802f7$0$8827$> Steven D'Aprano <> writes:

    >On Sat, 03 Apr 2010 22:58:43 +0000, kj wrote:


    >> Suppose I have a function with the following signature:
    >>
    >> def spam(x, y, z):
    >> # etc.
    >>
    >> Is there a way to refer, within the function, to all its arguments as a
    >> single list? (I.e. I'm looking for Python's equivalent of Perl's @_
    >> variable.)


    >Does this help?


    >>>> def spam(a, b, c=3, d=4):

    >... pass
    >...
    >>>> spam.__code__.co_varnames

    >('a', 'b', 'c', 'd')


    That's very handy. Thanks!

    >The hardest part is having the function know its own name.


    Indeed. Why Python does not provide this elmentary form of
    introspection as a built-in variable is extremely puzzling to me
    (even--no, *more so*--after reading PEP 3130).

    >I see that you are already using the inspect module. That almost
    >certainly is the correct approach. I'd be surprised if inspect is too
    >heavyweight, but if it is, you can pull out the bits you need into your
    >own function.


    That's a good idea. Thanks!

    ~K
    kj, Apr 5, 2010
    #12
  13. kj

    Steve Howell Guest

    On Apr 5, 11:49 am, kj <> wrote:
    > In <4bb802f7$0$8827$> Steven D'Aprano <> writes:
    >
    > >On Sat, 03 Apr 2010 22:58:43 +0000, kj wrote:
    > >> Suppose I have a function with the following signature:

    >
    > >> def spam(x, y, z):
    > >>     # etc.

    >
    > >> Is there a way to refer, within the function, to all its arguments as a
    > >> single list?  (I.e. I'm looking for Python's equivalent of Perl's @_
    > >> variable.)

    > >Does this help?
    > >>>> def spam(a, b, c=3, d=4):

    > >...     pass
    > >...
    > >>>> spam.__code__.co_varnames

    > >('a', 'b', 'c', 'd')

    >
    > That's very handy.  Thanks!
    >
    > >The hardest part is having the function know its own name.

    >
    > Indeed.  Why Python does not provide this elmentary form of
    > introspection as a built-in variable is extremely puzzling to me
    > (even--no, *more so*--after reading PEP 3130).
    >


    The Rejection Notice in the PEP certainly not give very many details
    for why the PEP was rejected.

    The first question in the Open Issues could easily be answered "yes."
    Steve Howell, Apr 6, 2010
    #13
  14. per-method jit compiler

    On 4 abr, 00:09, Steven D'Aprano <st...@REMOVE-THIS-
    cybersource.com.au> wrote:
    > On Sat, 03 Apr 2010 22:58:43 +0000, kj wrote:
    > > Suppose I have a function with the following signature:

    >
    > > def spam(x, y, z):
    > >     # etc.

    >
    > > Is there a way to refer, within the function, to all its arguments as a
    > > single list?  (I.e. I'm looking for Python's equivalent of Perl's @_
    > > variable.)

    >
    > Does this help?
    >
    > >>> def spam(a, b, c=3, d=4):

    >
    > ...     pass
    > ...>>> spam.__code__.co_varnames
    >
    > ('a', 'b', 'c', 'd')
    >
    > The hardest part is having the function know its own name.
    >
    > I see that you are already using the inspect module. That almost
    > certainly is the correct approach. I'd be surprised if inspect is too
    > heavyweight, but if it is, you can pull out the bits you need into your
    > own function.
    >
    > --
    > Steven


    The above post gave me an idea (very naive, of couse).
    What if I write a simple decorator to figure out the types of every
    function, and then we use it as a base for a simple method-jit
    compiler for python?

    example:

    def typer(f):
    def wrap(*args):
    a = f.func_code.co_varnames
    b = [type(i) for i in args]
    return dict(zip(a,b))
    return wrap

    @typer
    def spam(a, b, c=3, d=4):
    pass

    >>> spam(8,'hello',9.9, 10)

    {'a': <type 'int'>, 'c': <type 'float'>, 'b': <type 'str'>, 'd': <type
    'int'>}

    So by using this information, we record all the argument types used
    the first time each function/method is executed, and then we generate
    optimized code for them.
    From this point on, a guard should check if all arguments remain the
    same and, if so, the optimized code is run.
    Otherwise, just fall back to the interpreter.

    He! I have no idea how to implement it...
    Any guru out there?

    Luis
    Luis M. González, Apr 6, 2010
    #14
  15. Re: per-method jit compiler

    On Mon, 2010-04-05 at 22:45 -0700, Luis M. González wrote:
    > The above post gave me an idea (very naive, of couse).
    > What if I write a simple decorator to figure out the types of every
    > function, and then we use it as a base for a simple method-jit
    > compiler for python?

    I think its what done before by psyco:
    http://psyco.sourceforge.net/

    No need for a decorator though.

    --
    Florian Ludwig <>
    Florian Ludwig, Apr 6, 2010
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ken Varn
    Replies:
    2
    Views:
    612
    Ken Varn
    Jun 22, 2005
  2. Replies:
    3
    Views:
    481
    David Eppstein
    Sep 17, 2003
  3. Pierre Fortin

    args v. *args passed to: os.path.join()

    Pierre Fortin, Sep 18, 2004, in forum: Python
    Replies:
    2
    Views:
    667
    Pierre Fortin
    Sep 18, 2004
  4. er
    Replies:
    2
    Views:
    485
  5. Andrew Tomazos
    Replies:
    5
    Views:
    558
Loading...

Share This Page