-1

I'm used to a firm distinction between identifiers (variable names) and strings in programming languages.

Yet Python has some malleability between the two. For example in the code

import networkx as nx
G = nx.Graph()
G.add_edge(1, 2, food='eggs')
for node1, node2, data in G.edges(data=True):
    print(data['food'])

we use food as an identifier on line 3 and then as the string 'food' when we retrieve the attribute on line 5.

I'd like to better understand what is happening. What is the feature in a programming language generally and in Python specifically that makes this malleability possible?

(Please note that NetworkX is incidental to the question, which is why I'm not listing it in the keywords.)

Clarification why this question is not asking for an explanation of—and is all but superficially related to keyword arguments:

Sometimes what is happening is perfectly clear. For example if we write the code

def foo(bar, baz):
    pass

foo(1, 2)
foo(baz=2, bar=1)

(using keyword arguments in whichever order we like), the Python interpreter would have seen the names (not the strings) bar and baz and is henceforth expecting names (not strings) bar and baz, in any order.

But in other contexts this malleability is truly puzzling. For example in the following code explaining keyword arguments

def my_function(**kwargs):
    print str(kwargs)

my_function(a=12, b="abc")

{'a': 12, 'b': 'abc'}

we introduce keyword arguments in the function call, only, lo and behold, to find that they have become strings.

From an answer to the present question

>>> d = dict(food='eggs')
>>> d['food']
'eggs'

it becomes clear that in Python there is zero distinction between d1 and d2 whether we write:

d1 = {'food':'eggs'}

or

d2 = dict(food='eggs')

What happens with d1 makes perfect sense. The type of 'food' is a string.

But, again, d2 is obscure. Although we end up with exactly the same outcome, the language has somehow molded the identifier/name that we used (food) into a string.

What is the feature of programming languages generally and Python specifically that permits a language to mold a name into a string? Now that a pattern is emerging, might it be the case that Python uses some mechanism for switching names into strings, but there are no instances of switching strings to identifiers?

Community
  • 1
  • 1
Calaf
  • 10,113
  • 15
  • 57
  • 120
  • Its not really a string, but a key. – Craicerjack Aug 31 '16 at 15:38
  • 4
    The question asks what language feature makes it possible to use `add_edge(food='eggs')` and later retrieve the information using a string key in `data['food']`. The answer is *keyword arguments*. `food` in the call to `add_edge` is a keyword argument, so the function retrieves it inside a dictionary which has the string `food` as a key. So it can do whatever on the implementation side to make it available as a dictionary in `edges()`. That has nothing to do with this network library or whatever, it’s just keyword arguments, making this a fine duplicate of the linked question. – poke Aug 31 '16 at 15:39
  • 7
    So we un-dupe-hammered this question which was duped to a question about keyword arguments just so three users could provide an answer explaining keyword arguments? – Two-Bit Alchemist Aug 31 '16 at 15:43
  • *“But, again, d2 is obscure.”* – Not really. When passing keyword arguments to a function that takes `**kwargs`, all extraneous arguments are placed into a dictionary `kwargs`. And argument names are automatically the keys of said dictionary. `food` in `dict(food='eggs')` never was an identifier (like a variable name is), it’s just the name of the argument. And argument names are strings. – poke Aug 31 '16 at 16:17
  • See also: [Understanding kwargs in Python](http://stackoverflow.com/q/1769403/216074) – poke Aug 31 '16 at 16:19
  • Why is it so surprising that a dict literal and _the dict constructor_ both produce a dict, and that those dicts are equal if they are passed the same arguments? That's literally what you're saying with "d2 is obscure". – Two-Bit Alchemist Aug 31 '16 at 16:26
  • @poke "And arguments names are strings." That would just about settle the question. When I try `>>> def foo('bar'):`, I get `invalid syntax`, so there is more to it than that. If you understand what is happening, please write more. – Calaf Aug 31 '16 at 16:29
  • @Two-BitAlchemist Re: "Why is it so surprising... " I am as puzzled that you don't see what's puzzling me as you are puzzled at my puzzlement on this issue ( :) ). – Calaf Aug 31 '16 at 16:31
  • @Calaf You can't use strings as parameter names quite so directly. What you _can_ do is use `**` against an arg (usu. called "kwargs" but it doesn't matter) to indicate that it is a _dictionary of parameters_. That's what `**` is for -- it unpacks a dictionary into a function call. The key-value pairs in the dict become `param_name=param_value` pairs to the function. – Two-Bit Alchemist Aug 31 '16 at 16:31
  • So if you have `def fun(a=1, b=2)` you could call `fun(**{'a': 3, 'b': 4})` or `fun(a=3, b=4)`. Same thing. Alternatively if you have `def fun(**kw)` then inside the function you can reference the name `kw` just like any other parameter passed to the function, and it will be a dict. If you call _that_ definition like `fun(a=3, b=4)`, then `kw == {'a': 3, 'b': 4}`. – Two-Bit Alchemist Aug 31 '16 at 16:34
  • @Two-BitAlchemist Yes, yes, I understand the mechanics of what to plug where to get this or that kind of result. Yet I don't understand how what I'm doing can be permissible under the hood. Am I in the wrong forum? – Calaf Aug 31 '16 at 16:37
  • In C-land it uses a dictionary object for this. Any implementation of Python has access to all its basic types. To me it's no more interesting or surprising than the fact that it knows other arguments are strings, ints, or floats. Sorry I can't explain it better. – Two-Bit Alchemist Aug 31 '16 at 16:38
  • *“I don't understand how what I'm doing can be permissible under the hood”* – The Python language is [defined like that](https://docs.python.org/3/reference/compound_stmts.html#index-22). It does not matter how it’s implemented “under the hood”. It’s just how the language is designed. – poke Aug 31 '16 at 17:31

1 Answers1

3

A simpler example of this behaviour is a dictionary:

>>> d = dict(food='eggs')
>>> d['food']
'eggs'

Those keyword arguments are just a syntactic sugar that allows passing arbitrary arguments, so that they can be accessed by name in the called function, like:

def func(**kwargs):
    return kwargs['food']

>>> func(food='eggs')
'eggs'
bereal
  • 32,519
  • 6
  • 58
  • 104