Accessing a function within a function(nested function?) [duplicate] - python

This question already has answers here:
How to access a function inside a function?
(6 answers)
Closed 6 years ago.
Python noob here.
How do I get hold of the 'inner' function within the 'fib' function?
from time import sleep
class Fibonacci(object):
def __init__(self, a, b, limit=50):
self.a = a
self.b = b
self.limit = limit
def fib(self):
while self.a < self.limit:
c = self.a + self.b
sleep(1)
print self.a,
self.b = self.a
self.a = c
def inner(self):
print 'Damn it! Just print already!'
j = Fibonacci(0,1,2)
j.fib()
## This doesn't work. Gives an "AttibuteError: 'function' object has no attribute 'inner'"
j.fib.inner()

You cannot, not unless fib returns inner somehow. inner is essentially a local variable inside the scope of fib and you can't access a function's locals from outside of it. (That wouldn't even make sense, since the locals don't exist except when the function is running. Think about it -- would it make sense to access fib's c variable from outside of the function?)

Do not use the following.
[...]
>>> j = Fibonacci(0,1,2)
>>> j.fib()
0 1 1
>>> # dark magic begins!
>>> import new
>>> new.function(j.fib.im_func.func_code.co_consts[2],{})(None)
Damn it! Just print already!
You can tell simply by looking at it that it's not really Python, and for that matter it isn't really calling the "inner" function itself, it's simply creating a new function like it. I also didn't bother setting the globals 'correctly', because this is a terrible thing to do in the first place..
[I should mention that the point of the above is to note that the idea that you can't access internals from outside isn't strictly true, though it's almost never a good idea. Exceptions include interpreter-level code inspections, etc.]
Unclean! Unclean!

from time import sleep
class Fibonacci(object):
def __init__(self, a, b, limit=50):
self.a = a
self.b = b
self.limit = limit
def fib(self):
while self.a < self.limit:
c = self.a + self.b
sleep(1)
print self.a,
self.b = self.a
self.a = c
def inner(self):
print 'Damn it! Just print already!'
Fibonacci.fib.inner = inner
fib.inner = None
This code snippet will allow you to use inner.

The below seems to achieve what you want
from types import CodeType, FunctionType
def find_nested_func(parent, child_name):
""" Return the function named <child_name> that is defined inside
a <parent> function
Returns None if nonexistent
"""
consts = parent.func_code.co_consts
for item in consts:
if isinstance(item, CodeType) and item.co_name==child_name:
return FunctionType(item, globals())

As stated by some of the other readers, it's a problem of scope. FWIW, this works by returning the inner function:
from time import sleep
class Fibonacci(object):
def __init__(self, a, b, limit=50):
self.a = a
self.b = b
self.limit = limit
def fib(self):
while self.a < self.limit:
c = self.a + self.b
sleep(1)
print self.a,
self.b = self.a
self.a = c
def inner():
print 'Damn it! Just print already!'
return inner
j = Fibonacci(0,1,2)
j.fib()()
For reference, here's a good intro to python's scoping:
Short Description of the Scoping Rules?

Related

I made a basic calculator using class and format function in python giving no error also not running

What changes can be done so that I can print user friendly output using one print statement only ..... It would be helpful if format can help but right now no output is coming.....
class Calc:
def __init__(self,a,b): # we can pass other parameters also in the constructor for assigning values
self.a=(int)(input("Enter a"))
self.b=(int)(input("Enter b"))
def add(self):
return self.a+self.b
def sub(self):
return self.a-self.b
def mult(self):
return self.a*self.b
def div(self):
return self.a/self.b
def calculator(self):
print("""sum of {0} and {1}= {}
subtraction of {0} and {1}={}
Multiplication of {0} and {1}={}
Division of {0} and {1}={}""".format(c1.a,c1.b,c1.add(),c1.sub(),c1.mult(),c1.div()))
c1=Calc()
c1.calculator()
Class init-ing is def __init__(self), not def init(self) (two underscores _ on both sides).
Also, you don't need __init__(self, a, b), since you ask for a and b from the user anyway.
def __init__(self):
self.a = (int)(input("Enter A: "))
self.b = (int)(input("Enter B: "))
What you might not really need to look into right now, considering the level of this code, but should for the future, is checking whether the user input can be int. Otherwise it throws errors and doesn't work anyway.
Didn't realize the underscores were already there even after I myself reformatted the question's code part.
In any case, if You really want to do this this way, you need to
Either add arguments when instancing Calc() or remove the parameters:
class Calc:
def __init__(self):
...
c1 = Calc()
# Next one does NOT work
c3 = Calc(var1) # Too many arguments given
class Calc:
def __init__(self, a, b):
...
c1 = Calc(var1, var2)
# Next one does NOT work
c2 = Calc() # Too few arguments given
You can't create a new object of a class without matching its __init__() parameters unless the parameters are optional as such
class Ob:
def __init__(self, a=None, b=None):
...
c1 = Ob(var1) # a=var1, b=None
c2 = Ob(b=var2) # a=None, b=var2
c3 = Ob(var1, var2) # a=var1, b=var2
Formatting strings with this method You either have to use automatic field numbering or manual field specification:
# This does NOT work:
print("Sum of {0} and {1}= {}".format(self.a, self.b, self.add()))
# This one DOES work:
print("Sum of {0} and {1}= {2}".format(self.a, self.b, self.add()))
#This one also works:
print("Sum of {} and {}= {}".format(self.a, self.b, self.add()))
And as stated by #wovano, having input() in object initialization is bad practice alltogether. It'd be better, in Your code's case, to have an empty __init__() and separate function to call input()
def __init__(self):
pass
def ask_input(self):
self.a = (int)(input("Enter A: "))
self.b = (int)(input("Enter B: "))

Calling an object of a method of a class within another method of the same class

I was trying to call the objects of a method in one of my classes within antoher method of the same class. Below you can find a small example of how I tried to do it:
class example_class():
def some_method(self):
#... calculate something ...
a = 1
b = 2
def second_method(self):
call = self.some_method()
c = call.a + call.b
If I do this, I get the error: "'NoneType' object has no attribute 'a'".
I am sure this is a fearly basic problem, but I am using classes, objects and methods for the first time and would really appreciate the help!
Thank you in advance and stay safe!
class example_class():
def some_method(self):
#... calculate something ...
self.a = 1
self.b = 2
def second_method(self):
# call = self.some_method()
c = self.a + self.b
This should work
class example_class:
def some_method(self):
self.a = 1
self.b = 2
def second_method(self):
self.some_method()
print(self.a + self.b)
You can't access a method's local variables from another method, you should store them as attributes.

Get rid of small integer caching under python 2.7

I'm relying on id(obj) to distinguish two objects in my project. However, this doesn't work when objects are small integers unfortunately due to python's small value caching. The following two elements will have the same id.
self.a = 0
self.b = 0
# id(self.a) is the same as id(self.b)
Is there a way to make id(self.a) different from id(self.b) but still retain the syntax listed above?
Thanks!
EDIT:
See the following example
class B:
def __init__(self):
self.b = 0
def eat( self ):
self.b = 0
class A:
def __init__(self):
self.b = B()
self.a = 0
def eat1( self ):
self.a = 0
def eat2( self ):
self.b.b = 0
a = A()
a.b.eat()
a.eat2()
Basically a.b.eat() is accessing the same variable as a.eat2() and eat1() is accessing a different variable. However, self.b.b and self.a have the same id which makes it hard to distinguish between them.

returning instances in python

class classname():
def func(self,a,b):
self.c = a+b
self.d = a-b
self.e = a*b
return self
cn = classname()
This way i can access cn.c, cn.d and cn.e can i use something else other then self to return it and it will be a structure. I know its possible in matlab where you can define structure in a function. Something what i expect should look like this:
class classname():
def func(self,newself,a,b):
self.c = a+b
self.d = a-b
newself.e = a*b
return self, newself
cn = classname()
I know this is not a valid code but just an idea what i want from code.
I think what you want is this:
class classname:
def __init__(self, a, b):
self.c = a+b
self.d = a-b
self.e = a*b
cn = classname(12, 34) # Just random values for 'a' and 'b'. Use whatever you like!
print(cn.c)
>>> 46
print(cn.d)
>>> -22
print(cn.e)
>>> 408
The __init__ function is automatically called when the object is created. Self will always refer to the object, so adding attributes to it will add it to the object, so you don't need to return anything.

Calling the same method in all the objects from a class

I'm quite new using Python and can't find the answer to this.
Let's souppose I have a code like:
class numbers():
def __init__(self,a,b):
self._a = a
self._b = b
def add(self):
self._suma = self._a + self._b
After that, I create a lot of instances of numbers:
obj1 = numbers(1,2)
obj2 = numbers(7,16)
...
Then, I want to call the add method in all the objects of the class numbers in a simple and clean way.
Notice that if I instantiated numbers class 1000 times I don't want to write 1000 times this
objX.add()
I looked for an answer in the web and I found that, in other lenguages, they put all the names of the objects in a string, and then iterates on it calling the method.
The problem is I donĀ“t know how to do that in python, nor if it's the best way to solve this problem.
Thank you
create a list of objects.
objs = []
add elements to this list
obj1 = numbers(1,2) # say these are your objects
obj2 = numbers(7,16)
...
objs.append(obj1) # add them to the list
call the add method for each element in the list
for obj in objs:
obj.add() # call the add method.
Well you'll need to have some way to find all the numbers objects. For clarity, I'll adjust your code naming conventions slightly so they're more standard (per PEP9).
class Number(object):
def __init__(self, a, b):
self._a = a
self._b = b
def add(self):
self.sum = self._a + self._b
return self.sum # not really sure where we're using it, so here?
a = Number(1, 2)
b = Number(2, 3)
c = Number(3, 4)
Now we have three objects, a, b, and c, that are all Number objects. There's two ways to get a list of them, and one is really bad. We'll go over that one first.
number_objs = [obj for obj in globals() if isinstance(obj, Number)]
for number in number_objs:
number.add()
This queries the all the objects currently in the namespace to see if they're Numbers. The problem with doing it this way is that you lose encapsulation. You probably don't want to rely on your functions finding a number object by calling globals(). Instead, let's give Number an encompassing object!
class NumberList(list):
# this is literally just a list, but we want to add one method:
def make_number(a, b):
number = Number(a, b)
self.append(number)
all_numbers = NumberList()
a = all_numbers.make_number(1, 2)
b = all_numbers.make_number(2, 3)
c = all_numbers.make_number(3, 4)
for number in all_numbers:
number.add()
Alternatively you can give Number a classmethod that works as an alternate constructor, but also adds it to a list. This is probably the cleanest way to handle it.
class Number(object):
def __init__(self, a, b):
self._a = a
self._b = b
def add(self):
self.sum = self._a + self._b
return self.sum
#classmethod
def track(cls, a, b, container):
n = cls(a, b)
container.append(n)
return n
all_numbers = []
a = Number.track(1, 2, all_numbers)
b = Number.track(2, 3, all_numbers)
c = Number.track(3, 4, all_numbers)
for number in all_numbers:
number.add()
You need to append each obj to a list. To automate that, simply create an empty list and write the code inside the init. This will run automatically every time a new object is created
class numbers():
def __init__(self,a,b):
self.a = a
self.b = b
listObjs.append(self)
def Add(self):
return self.a + self.b
listObjs = []
ob1 = numbers(4,5)
ob2 = numbers(4324,5)
ob3 = numbers(1,25)
ob4 = numbers(2,5324)
ob5 = numbers(21,5)
ob6 = numbers(4213,54)
Then simply make a loop and print the obj.Add(). This will run for each obj in the list.
for obj in listObjs:
print(obj.Add())
Output:
9
4329
26
5326
26
4267

Categories

Resources