9

In the question What does the "yield" keyword do?, I found a Python syntax being used that I didn't expect to be valid. The question is old and has a huge number of votes, so I'm surprised nobody at least left a comment about this function definition:

def node._get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
       yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
       yield self._rightchild  

What I tried to get this sort of syntax evaluated:

  • assigning an attribute to a class or object
  • redefining a function of an imported module

fails so far with

SyntaxError: invalid syntax

I looked up the link (maybe outdated) given in the question, and searched the web for the usage of def, but I found nothing explaining this "dotted name" pattern. I'm using Python 3, maybe this is a feature of Python 2?

Is (or was) this syntax valid, if yes what does it mean?

Wolf
  • 9,679
  • 7
  • 62
  • 108
  • 1
    https://docs.python.org/3/reference/lexical_analysis.html#identifiers, so no. – Padraic Cunningham Oct 05 '16 at 10:17
  • 1
    If you look at the code you also you see `node = candidates.pop()` then you see `node._get_child_candidates`, it is a typo in the function. They are calling the method `get_child_candidates` on the node instance. – Padraic Cunningham Oct 05 '16 at 10:23
  • @PadraicCunningham well, a typo in the very case I referred to, but also invalid syntax in all cases (I tried to figure out the [identifier syntax you mentioned](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) but found the `Pc`rule a little confusing)? – Wolf Oct 05 '16 at 10:30
  • 1
    the only difference between python2 and python3 is *Python 3.0 introduces additional characters from outside the ASCII range* i.e non-ascii identifiers, the pep https://www.python.org/dev/peps/pep-3131/ goes through it in detail – Padraic Cunningham Oct 05 '16 at 10:34

2 Answers2

4

No, the syntax is not valid. It is easy to prove by checking the documentation. In Python 2, an identifier is constructed by the following rules:

identifier ::=  (letter|"_") (letter | digit | "_")*
letter     ::=  lowercase | uppercase
lowercase  ::=  "a"..."z"
uppercase  ::=  "A"..."Z"
digit      ::=  "0"..."9"

In Py3 the rules are more or less the same, beside being expanded up to the range of Unicode characters.

It seems that the author probably meant something like

class Node:
    ...
    def _get_child_candidates(self, ...):
        ...
Zaur Nasibov
  • 22,280
  • 12
  • 56
  • 83
3

As in my comment you cannot, the valid identifiers for python3 are in the docs:

Identifiers (also referred to as names) are described by the following lexical definitions.

The syntax of identifiers in Python is based on the Unicode standard annex UAX-31, with elaboration and changes as defined below; see also PEP 3131 for further details.

Within the ASCII range (U+0001..U+007F), the valid characters for identifiers are the same as in Python 2.x: the uppercase and lowercase letters A through Z, the underscore _ and, except for the first character, the digits 0 through 9.

Python 3.0 introduces additional characters from outside the ASCII range (see PEP 3131). For these characters, the classification uses the version of the Unicode Character Database as included in the unicodedata module.

If you examine the code you can see it is a typo in the original question:

def node._get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

And this is the caller:

result, candidates = list(), [self]
while candidates:
    node = candidates.pop() # creates an instance
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    # the _get_child_candidates node is called 
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

So the method _get_child_candidates is called on the instance. So really the actual code looks like:

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

And this is the caller:

result, candidates = list(), [self]
while candidates:
    node = candidates.pop() # creates an instance
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    # the _get_child_candidates node is called 
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321