There have been several discussions about unexpected behavior when people write programs within a class definition. Some suggest various ways around the problem and that is fine although I bet new people will keep encountering this issue.
I have some questions about what people want to do with this kind of programming and for each use, there may be suggested ways to get things done and get the right result in a reasonably natural way. I have some overall concerns regarding the aggressive approach taken in Python toward supporting too many features in what is loosely considered object-oriented programming to the point where some combinations interact in mathematically intractable ways resulting in concessions and compromises such as the one we see. Question 1: Do you want evaluation at class definition time or at run time? Question 2: Do you want the variables available at the class level or at the instance level? Question 3: Which python variations on syntactic sugar, such as list comprehensions, get expanded invisibly in ways that make the problem happen by asking for variables to be found when no longer in the visible range? There may be matters of efficiency some would consider but some of the examples seen recently seemed almost silly and easy to compute. The people asking about this issue wanted to define a bunch of CONSTANTS, or things that might as well be constants, like this: def Foo(): A = ("male", "female", "other") B = [ kind[0] for kind in A ] # First letters only # And so on making more constants like a dictionary mapping each string to a number or vice versa. All the above can be evaluated at the time the class is defined but unintuitive scope rules make some operations fail as variables defined in the scope become unavailable to other things that SEEM to be embedded in the same scope. So, are these constants being called as Foo.A or are they used after an instance is created like: Aleph = Foo() # Use Aleph.A If they are ONLY to be used within an instance of Foo or invoked from within there, there may be a fairly simple suggestion. If you already have a __init__ method, then instantiate the variables there carefully using the self object to reference those needed. def Foo(): def __init__(self): # calculate A and B and whatever. self.A = A self.B = B # . These calculations would not be at declaration time for class Foo but the results would be created every time an object of class Foo came along. If Foo is subclassed, it would be wise to make sure the initialization reaches up to run Foo.__init__ properly too. Within __init__ I presume all the USUAL scoping rules make sense so if making a B includes using A, it would easily be found there first. Again, not self.A, just A at that stage. You can create self.A near the end of the initialization when all calculations needed are complete. Second suggestion, even if what is being made may not be constants. Create a function either outside the class or defined within. Have it do any internal calculations you need in which all internal variables can play nicely with each other. Then let it return all the variables in a tuple like this: def make_sexual_constants(): A = . B = . C = f(A,B) D = . def Foo(): (A, B, C, D) = make_sexual_constants(): Can we agree that the class Foo now has those 4 variables defined and available at either the class level or sub-class or instance levels? But the values are created, again, in a unified safe environment? As noted in section 3, it would be good to know what python features may be unsafe in this kind of context. I had an unrelated recent discussion where it was mentioned that some proposed feature changes might not be thread safe. Valid consideration when that may lead to hard-to-explain anomalies. We now hear that because a list comprehension can be unwound internally into a "while" loop and an "if" statement and that some parts may expand to calls to a "range" statement, perhaps some variables are now in more deeply embedded contexts that have no access to any class variables. I think that is quite reasonable; hence my suggestion we need to know which ones to avoid, or use a workaround like expanding it out ourselves and perhaps carefully import variables into other contexts such as by passing the variable into the function that otherwise cannot access it from a point it can still be seen. Least, but at least last, I ask if the need really exists for these variables as constants versus functions. If creating C this way runs into problems, but A and B are fine, consider making a method with some name like Foo.get_C() that can see A and B and do the calculation and yet return the value of C needed. Less efficient but . I apologize for the length but assure you I normally would say much more. Avi -- https://mail.python.org/mailman/listinfo/python-list