0

The expression logic=_changelog_txt:

def writeChangelog(repo, milestone, overwrite=False, extension=u'.txt',
                   logic=_changelog_txt): # HERE
    """Write 'Changelog - <milestone>.txt'"""
    outFile = _outFile(dir_=CHANGELOGS_DIR,
                       name=u'Changelog - ' + milestone.title + extension)
    if os.path.isfile(outFile) and not overwrite: return outFile
    issues = getClosedIssues(repo, milestone, skip_labels=SKIP_LABELS)
    return logic(issues, milestone, outFile)

def writeChangelogBBcode(repo, milestone, overwrite=False):
    """Write 'Changelog - <milestone>.bbcode.txt'"""
    return writeChangelog(repo, milestone, overwrite, extension=u'.bbcode.txt',
                          logic=_changelog_bbcode) # no errors here

def _changelog_txt(issues, milestone, outFile):
    with open(outFile, 'w') as out:
        out.write(h2(_title(milestone)))
        out.write('\n'.join(ul(issues, closedIssue)))
        out.write('\n')
    return outFile

gives me Unresolved reference \_changelog\_txt. What is the most pythonic way to do what I want ? See also: What is the best way to pass a method (with parameters) to another method in python

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
  • You need to define the `_changelog_txt` method before the `writeChangelog` method because default argument are instanciated when the function is defined, so at this moment `_changelog_txt` does not exist. – Holt Jul 01 '14 at 12:43
  • Has it anything to do with PyCharm? Otherwise you should get a `NameError: name '_changelog_txt' is not defined` – Germano Jul 01 '14 at 12:44
  • @Germano: yep I use PyCharm :) – Mr_and_Mrs_D Jul 01 '14 at 12:46
  • Then try this one: http://stackoverflow.com/questions/20479696/pycharm-unresolved-reference-error-on-the-ide-when-opening-a-working-project – Germano Jul 01 '14 at 12:49
  • @Germano: no need - I just rearranged the methods - :D – Mr_and_Mrs_D Jul 01 '14 at 12:52

2 Answers2

2

This is a matter of order. As _changelog_txt is not yet defined when you define function `writeChangelog, it throws an error.

This works:

def b(s):
    print s

def a(f=b):
    f("hello")

a()

This does not:

def a(f=b):
    f("hello")

def b(s):
    print s

a()

It should be noted that this has nothing to do with the keyword argument default value being a function. It could be any other object undefined before defining the function. There is no such thing as _changelog_txt when the interpreter encounters the def writeChangelog.

Reordering the code is a good alternative in this case.

The situation during runtime is different, as before running anything the interpreter has already encountered all defs. That is why one seldom bumps into this kind of problems with python.

DrV
  • 22,637
  • 7
  • 60
  • 72
0

As an addition to DrV's answer:

In Python, a function's signature is evaluated when the interpreter sees it for the first time and not at the time of calling. So, in terms of scope, your code is equivalent to the following:

b = a
a = 1

Output:

b = a
NameError: name 'a' is not defined

I hope you understand, now, why your code doesn't work.

On a side note: While this behavior makes the scope in which default parameter expressions are evaluated much more obvious, it is also an easy source of bugs, e.g.:

def foo(bar = []):
    bar.append(1)
    return bar

print(foo())
print(foo())

Output:

[1]
[1, 1]

Here, the default value is always the same list – across all calls of foo – because the function signature is evaluated only once. The solution is to use None as default value and do an explicit check:

def foo(bar = None):
    if bar is None:
        bar = []
    bar.append(1)
    return bar

print(foo())
print(foo())

Output:

[1]
[1]
balu
  • 3,500
  • 4
  • 34
  • 35