Okay, bear with me on this, I know it's going to look horribly convoluted, but please help me understand what's happening.
from functools import partial
class Cage(object):
def __init__(self, animal):
self.animal = animal
def gotimes(do_the_petting):
do_the_petting()
def get_petters():
for animal in ['cow', 'dog', 'cat']:
cage = Cage(animal)
def pet_function():
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
funs = list(get_petters())
for name, f in funs:
print name + ":",
f()
Gives:
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
So basically, why am I not getting three different animals? Isn't the cage 'packaged' into the local scope of the nested function? If not, how does a call to the nested function look up the local variables?
I know that running into these kind of problems usually means one is 'doing it wrong', but I'd like to understand what happens.
The nested function looks up variables from the parent scope when executed, not when defined.
The function body is compiled, and the 'free' variables (not defined in the function itself by assignment), are verified, then bound as closure cells to the function, with the code using an index to reference each cell. pet_function thus has one free variable (cage) which is then referenced via a closure cell, index 0. The closure itself points to the local variable cage in the get_petters function.
When you actually call the function, that closure is then used to look at the value of cage in the surrounding scope at the time you call the function. Here lies the problem. By the time you call your functions, the get_petters function is already done computing it's results. The cage local variable at some point during that execution was assigned each of the 'cow', 'dog', and 'cat' strings, but at the end of the function, cage contains that last value 'cat'. Thus, when you call each of the dynamically returned functions, you get the value 'cat' printed.
The work-around is to not rely on closures. You can use a partial function instead, create a new function scope, or bind the variable as a default value for a keyword parameter.
Partial function example, using functools.partial():
from functools import partial
def pet_function(cage=None):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
Creating a new scope example:
def scoped_cage(cage=None):
def pet_function():
print "Mary pets the " + cage.animal + "."
return pet_function
yield (animal, partial(gotimes, scoped_cage(cage)))
Binding the variable as a default value for a keyword parameter:
def pet_function(cage=cage):
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
There is no need to define the scoped_cage function in the loop, compilation only takes place once, not on each iteration of the loop.
My understanding is that cage is looked for in the parent function namespace when the yielded pet_function is actually called, not before.
So when you do
funs = list(get_petters())
You generate 3 functions which will find the lastly created cage.
If you replace your last loop with :
for name, f in get_petters():
print name + ":",
f()
You will actually get :
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
This stems from the following
for i in range(2):
pass
print(i) # prints 1
after iterating the value of i is lazily stored as its final value.
As a generator the function would work (i.e. printing each value in turn), but when transforming to a list it runs over the generator, hence all calls to cage (cage.animal) return cats.
Let's simplify the question. Define:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Then, just like in the question, we get:
>>> for name, f in list(get_petters()):
... print(name + ":", f())
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
But if we avoid creating a list() first:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
What's going on? Why does this subtle difference completely change our results?
If we look at list(get_petters()), it's clear from the changing memory addresses that we do indeed yield three different functions:
>>> list(get_petters())
[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]
However, take a look at the cells that these functions are bound to:
>>> for _, f in list(get_petters()):
... print(f(), f.__closure__)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
>>> for _, f in get_petters():
... print(f(), f.__closure__)
Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)
For both loops, the cell object remains the same throughout the iterations. However, as expected, the specific str it references varies in the second loop. The cell object refers to animal, which is created when get_petters() is called. However, animal changes what str object it refers to as the generator function runs.
In the first loop, during each iteration, we create all the fs, but we only call them after the generator get_petters() is completely exhausted and a list of functions is already created.
In the second loop, during each iteration, we are pausing the get_petters() generator and calling f after each pause. Thus, we end up retrieving the value of animal at that moment in time that the generator function is paused.
As #Claudiu puts in an answer to a similar question:
Three separate functions are created, but they each have the closure of the environment they're defined in - in this case, the global environment (or the outer function's environment if the loop is placed inside another function). This is exactly the problem, though -- in this environment, animal is mutated, and the closures all refer to the same animal.
[Editor note: i has been changed to animal.]
Related
I'm just writting an Exception, which should stores a Class object as a field for the error message process.
class BadType < Exception
getter should_be : Class
getter actual : Class
end
def feed(pet : Animal, food : Food)
raise BadType.new should_be: Cat, actual: pet.class if food == "fish" && !pet.is_a?(Cat)
end
But, Class is abstract so cannot be used here as a field type.
So, how to solve this in my case? I hadn't found any derived class of Class, so does it mean one can never store a Class object as a field? But here my problem is a meaningful usage for this (any type check depends on input may require this BadType).
I'm not sure whether I missed something, so I came here first.
Class can't (yet) be used as a ivar type. Maybe it never will, I don't know.
But you can actually use generics for concrete data types, inferred from the arguments to the constructor:
# possible workaround
class Foo(T, U)
def initialize(#bar : T, #baz : U)
end
end
Foo.new String, Int32
I don't know your exact use case, but chances are you don't really need these values as classes. You probably can't do much with it anyway and drawing from your example I guess it's mostly for showing debugging information.
So it is very likely that just storing the names of the classes (as strings) would be a better solution for this problem.
# better solution
class Foo
#bar : String
#baz : String
def initialize(bar : Class, baz : Class)
#bar = bar.name
#baz = baz.name
end
end
Foo.new String, Int3
The generic arguments mean a new concrete type is created for every combination of classes used with Foo. That can have an impact on compiler performance.
I would most definitely use strings for this. Even if you need the classes for some particularly processing later on, it's probably better to just map the strings to the constants using a macro-generated lookup table.
Try generics:
class BadType(ClassA, ClassB) < Exception
getter should_be : ClassA
getter actual : ClassB
def initialize(#should_be, #actual)
#message = "Bad type: should be #{#should_be}, actual is #{#actual}"
end
end
def feed(pet : Animal, food : Food)
raise BadType(Animal.class, Animal.class).new should_be: Cat, actual: pet.class if food == "fish" && !pet.is_a?(Cat)
end
class Animal
end
record Food, food : String do
def ==(other_food)
#food == other_food
end
end
class Cat < Animal
end
class Dog < Animal
end
feed pet: Dog.new, food: Food.new("fish")
Output:
Unhandled exception: Bad type: should be Cat, actual is Dog (BadType(Animal:Class, Animal:Class))
from /eval:11:3 in 'feed'
from /eval:29:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:104:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:93:7 in 'main'
from /usr/lib/crystal/crystal/main.cr:133:3 in 'main'
from __libc_start_main
from _start
from ???
Demo: https://carc.in/#/r/4pgs
class Pizza(object):
radius = 2
#classmethod
def get_radius(self):
return self.radius
>>>print Pizza.get_radius is Pizza().get_radius
False
I think the result is True,because the classmedthod belongs to the class object.
When you instanciate Pizza you get a get_radius function which has a different id but points to the same code:
>>> id(Pizza().get_radius)
50027464
>>> id(Pizza.get_radius)
41275656
ok the refs are different but the contents are the same:
>>> Pizza.get_radius.__func__ is Pizza().get_radius.__func__
True
the function objects are the same and using == also yields True:
>>> Pizza().get_radius == Pizza.get_radius
True
So, like when comparing strings or integers, or whatever, it's better to avoid is because it's too much implementation dependent, with minimal benefits (except for the singletons like None)
>>> Pizza.get_radius()
2
>>> Pizza().get_radius
<bound method type.get_radius of <class 'Pizza'>>
When you type Pizza.get_radius(),you call the function and you get your result.
When you type Pizza().get_radius ,first of all you initiate a new Pizza object and you don't call the function really.
I am a beginner in Python and I am trying to understand trees better. Why can't I add nodes inside the function f below? I would expect that the final print returns the value 2 instead of an error that f(test) in None.
class Testcls(object):
def __init__(self,data):
self.data= data
self.children = []
def add_child(self,obj):
self.children.append(obj)
def f(test):
data1 = test.data+1
test.add_child(Testcls(data1))
test = Testcls(1)
print f(test).data
Final print in your case should actually return an error. Function f doesn't return anything (hence the None if you type print f(test). To add children using f you can add a return statement:
def f(test):
data1 = test.data+1
test.add_child(Testcls(data1))
return test
Which now returns the object test which you can further access and process,
f(test)
print test.children[0].data
Since it actually returns the test object you can also call it and use it directly:
print (f(test).children)[0].data
You don't have to have a return statement either - f will still change the object you're sending to it. In case you don't want to return the object, you just need to call the function separately and only then use test like shown in the second code snippet.
I use an interactive interpreter for data analysis, so I tend to just define functions and call them when I need them. In this case, I would like to call some function:
def function(var):
do stuff
ax.plot(x, y)
blah
ax.set_title('var')
where var in the function call is ndarray and var in set_title is the reference of the ndarray.
I'm currently implementing this with
def function(var, varstring):
do stuff
ax.plot(x, y)
blah
ax.set_title(varstring)
where var is foo and varstring is just 'foo' - an example call:
module.function(foo, 'foo')
now, my function takes many more variables and it's unwieldy to duplicate everything over and over again, but I can't figure out how to just get var as 'var' .. any attempt to do so provides me with a string of the ndarray values.
If you're working in the interpreter, the variables are likely in the global scope. Thus, you can access them using var = globals()[varstring] and then pass in the string name into each function.
Note that an object (var) has no knowledge of its names (varstring) which is why the global name needs to be passed to the function rather than the object itself.
you have to define your function in this way:
def function(varstring):
var = globals()[varstring]
# do stuff with `var` variable
ax.plot(x, y)
ax.set_title(varstring)
and then call module.function('foo')
So I have the following class object:
class Bond(object):
def __init__(self, Atom1=None, Atom2=None):
self.atoms = [Atom1, Atom2]
where Atom1 and Atom2 are mutable objects.
and I have:
>>> first_bond
Bond(Atom1, Atom2)
>>> second_bond
Bond(Atom1, Atom3)
>>> third_bond
Bond(Atom2, Atom1)
and also have:
>>> bonds
[first_bond, second_bond, third_bond]
If you realize, the first_bond and third_bond are the same since one is the reverse of the other, this is:
>>> first_bond == third_bond[::-1]
True
So my question is how can I implement a function or something that can filter only distinct objects, so that my final bonds is:
>>> bonds
[first_bond, second_bond]
I have read that maybe using __eq__ and __hash__ method would be a solution, and then using set(bonds). But since Atoms are mutable objects I don't know if this is kind of possible.