6

I was writing a small file utility earlier, and ran into an issue with passing by reference. After reading How do I pass a variable by reference?, I set the variable I wanted to pass through as an argument and also as the return value. Within the code below, it is the line:

diff = compareDir(path0, path0List, path1, path1List, diff)

where diff is the variable I wished to pass by reference.

While this works, it feels rather awkward. I think there must be a better way. In many other languages, I could just set compareLists() to have no return value, and use the side-effect of modifying the pass-by-reference argument. Python's pass-by-assignment seems to disallow this.

I am relatively new to python, and would like to know if there is a more pythonic way to resolve my issue. Would it require rethinking the functions entirely? Or is there a nice statement I am unaware of? I'd like to stay away from global variables.

I welcome any and all constructive criticisms and comments. Thanks!

Relevant Code:

def comparePaths(path0, path1):
    path0List = os.listdir(path0)
    path1List = os.listdir(path1)

    diff = False
    diff = compareDir(path0, path0List, path1, path1List, diff)
    print()
    diff = compareDir(path1, path1List, path0, path0List, diff)
    return diff

def compareDir(basePath, baseList, comparePath, compareDir, diffVar):
    for entry in baseList:
        #compare to the other folder
        if (not (entry in compareDir)):
            if (not (diffVar)):
                diffVar = True
                print ("Discreptancies found. The following files are different:")
                print (str(entry) + " doesn\'t exist in " + str(comparePath))
            else:
                print (str(entry) + " doesn\'t exist in " + str(comparePath))
    return diffVar
Community
  • 1
  • 1
Grey_Ham
  • 63
  • 4
  • 1
    If you want to cut down the parameters to something where you just call `compareLists()`, I think you may need to reconsider your approach. Perhaps by storing your values in a dictionary because that way you can modify the key/value pairs anywhere in the namespace. – Dan Dec 18 '15 at 05:08
  • Thanks Dan, that is an excellent suggestion. I may do just that. – Grey_Ham Dec 18 '15 at 05:37
  • The linked answer does a perfectly good job of explaining the distinction between mutable and immutable objects. `diff=False` is immutable. `diff=[False]` is mutable. – hpaulj Dec 18 '15 at 05:45
  • Just to be clear @hpaulj diff=[False] is using a class wrapper per the accepted answer's last suggestion in the linked question, correct? Or am I missing something in your notation there? – Grey_Ham Dec 18 '15 at 05:50

2 Answers2

3

Since in Python, the bool type is by definition immutable, the only way to modify a bool variable inside a function without reassigning it (and without defining it as a global variable) is to store it in a mutable type instance. ie:

  • Storing it in a mutable data structure (list, dict, ...) and pass this data structure to the function.
  • Having it as an attribute of a mutable object, and pass this object to the function.
Dim'
  • 518
  • 6
  • 12
  • Thanks Dim. You wouldn't happen to have a pydocs reference to mutable vs immutable types? I found [this reference on mutable vs. immutable sequences](https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy) but could use more info. – Grey_Ham Dec 18 '15 at 05:45
  • 1
    No, but I think everything is there: - numeric types (int, float, complex) are immutables. - str, bytes and tuple are immutable sequences. - list and bytearray are mutables. - set is mutable, frozenset is immutable. - dict is mutable. – Dim' Dec 18 '15 at 05:52
  • Thanks @Dim I appreciate your help. – Grey_Ham Dec 18 '15 at 05:55
2

Your problem has multiple possible solutions.

You can add nonlocal modifier (global prior to python3) for your diff variable to modify from inside function and have changes visible from outside.

    diff = False

    def compareDir(basePath, baseList, comparePath, compareDir):
        nonlocal diff
        for entry in baseList:
                    ...
                    diff = True

    compareDir(path0, path0List, path1, path1List)
    print()
    compareDir(path1, path1List, path0, path0List)
    return diff

Or you can have OOP solution with differ object and self.diff as explicit state of that object.

class differ(object):
    def __init__(self):
        self.diff = False
    def compareDir(self, basePath, baseList, comparePath, compareDir):
        ...
              self.diff = True
        ...

    def comparePaths(self, path0, path1):

Latter solution is super helpful if you need to do a lot of work in some 'context' and frequently need to change shared state.

hamilyon
  • 381
  • 1
  • 8