(background first: I am NEW to programming and currently in my very first "intro to programming class" in my college. This is our second assignment dealing with Functions. So far functions have been a pain in the ass for me because they don't really make any sense. ex: you can use miles_gas(gas) but then don't use "miles_gas" anywhere else, but the program still runs??, anyways)
Okay, I've looked EVERYWHERE online for this and can't find an answer. Everything is using "Exceptions" and "try" and all that advanced stuff. I'm NEW so I have no idea what exceptions are, or try, nor do I care to use them considering my teacher hasn't assigned anything like that yet.
My project is to make a program that gives you the assessment value, and the property tax upon entering your property price. Here is the code I came up with (following the video from my class, as well as in the book)
ASSESSMENT_VALUE = .60
TAX = 0.64
def main():
price = float(input('Enter the property value: '))
show_value(value)
show_tax(tax)
def show_value():
value = price * ASSESSMENT_VALUE
print('Your properties assessment value is $', \
format(value, ',.2f'), \
sep='')
def show_tax(value,TAX):
tax = value * TAX
print('Your property tax will be $', \
format(tax, ',.2f'), \
sep='')
main()
Upon running it, I get it to ask "blah blah enter price:" so I enter price then I get a huge red error saying
Traceback (most recent call last):
File "C:/Users/Gret/Desktop/chapter3/exercise6.py", line 41, in <module>
main()
File "C:/Users/Gret/Desktop/chapter3/exercise6.py", line 24, in main
show_value(value)
NameError: name 'value' is not defined
But I DID define 'value'... so why is it giving me an error??
Python is lexically scoped. A variable defined in a function isn't visible outside the function. You need to return values from functions and assign them to variables in the scopes where you want to use the values. In your case, value is local to show_value.
When you define a function, it needs parameters to take in. You pass those parameters in the brackets of the function, and when you define your function, you name those parameters for the function. I'll show you an example momentarily.
Basically what's happened is you've passed the function a parameter when you call it, but in your definition you don't have one there, so it doesn't know what to do with it.
Change this line:
def show_value():
To this line:
def show_value(price):
And show_value to show_value(price)
For example:
In this type of error:
def addition(a,b):
c = a + b
return c
addition() # you're calling the function,
# but not telling it the values of a and b
With your error:
def addition():
c = a + b
return c
addition(1,2) # you're giving it values, but it
# has no idea to give those to a and b
The thing about functions, is that those variable only exist in the function, and also the name of the parameters doesn't matter, only the order. I understand that's frustrating, but if you carry on programming with a more open mind about it, I guarantee you'll appreciate it. If you want to keep those values, you just need to return them at the end. You can return multiple variables by writing return c, a, b and writing the call like this sum, number1, number2 = addition(1,2)
Another problem is that I could call my addition function like this:
b = 1
a = 2
addition(b,a)
and now inside the function, a = 1 and b = 2, because it's not about the variable names, it's about the order I passed them to the function in.
You also don't need to pass TAX into show_tax because TAX is already a global variable. It was defined outside a function so it can be used anywhere. Additionally, you don't want to pass tax to show_tax, you want to pass value to it. But because show_value hasn't returned value, you've lost it. So return value in show value to a variable like so value = show_value(price).
Related
I have a dataset and I want to make a function that does the .get_dummies() so I can use it in a pipeline for specific columns.
When I run dataset = pd.get_dummies(dataset, columns=['Embarked','Sex'], drop_first=True)
alone it works, as in, when I run df.head() I can still see the dummified columns but when I have a function like this,
def dummies(df):
df = pd.get_dummies(df, columns=['Embarked','Sex'], drop_first=True)
return df
Once I run dummies(dataset) it shows me the dummified columsn in that same cell but when I try to dataset.head() it isn't dummified anymore.
What am I doing wrong?
thanks.
You should assign the result of the function to df, call the function like:
dataset=dummies(dataset)
function inside them have their own independent namespace for variable defined there either in the signature or inside
for example
a = 0
def fun(a):
a=23
return a
fun(a)
print("a is",a) #a is 0
here you might think that a will have the value 23 at the end, but that is not the case because the a inside of fun is not the same a outside, when you call fun(a) what happens is that you pass into the function a reference to the real object that is somewhere in memory so the a inside will have the same reference and thus the same value.
With a=23 you're changing what this a points to, which in this example is 23.
And with fun(a) the function itself return a value, but without this being saved somewhere that result get lost.
To update the variable outside you need to reassigned to the result of the function
a = 0
def fun(a):
a=23
return a
a = fun(a)
print("a is",a) #a is 23
which in your case it would be dataset=dummies(dataset)
If you want that your function make changes in-place to the object it receive, you can't use =, you need to use something that the object itself provide to allow modifications in place, for example
this would not work
a = []
def fun2(a):
a=[23]
return a
fun2(a)
print("a is",a) #a is []
but this would
a = []
def fun2(a):
a.append(23)
return a
fun2(a)
print("a is",a) #a is [23]
because we are using a in-place modification method that the object provided, in this example that would be the append method form list
But such modification in place can result in unforeseen result, specially if the object being modify is shared between processes, so I rather recomend the previous approach
I know the motto is "we're all consenting adults around here."
but here is a problem I spent a day on. I got passed a class with over 100 attributes. I had specified one of them was to be called "run_count". The front-end had a place to enter run_count.
Somehow, the front-end/back-end package people decided to call it "run_iterations" instead.
So, my problem is I am writing unit test software, and I did this:
passed_parameters.run_count = 100
result = do_the_thing(passed_parameters)
assert result == 99.75
Now, the problem, of course, is that Python willingly let me set this "new" attribute called "run_count". But, after delving 10 levels down into the code, I discover that the function "do_the_thing" (obviously) never looks at "run_count", but uses "passed_paramaters.run_iterations" instead.
Is there some simple way to avoid allowing yourself to create a new attribute in a class, or a new entry in a dictionary, when you naievely assume you know the attribute name (or the dict key), and accidentally create a new entry that never gets looked at?
In an ideal world, no matter how dynamic, Python would allow you to "lock" and object or instance of one. Then, trying to set a new value for an attribute that doesn't exist would raise an attribute error, letting you know you are trying to change something that doesn't exist, rather than letting you create a new attribute that never gets used.
Use __setattr__, and check the attribute exists, otherwise, throw an error. If you do this, you will receive an error when you define those attributes inside __init__, so you have to workaround that situation. I found 4 ways of doing that. First, define those attributes inside the class, that way, when you try to set their initial value they will already be defined. Second, call object.__setattr__ directly. Third, add a fourth boolean param to __setattr__ indicating whether to bypass checking or not. Fourth, define the previous boolean flag as class-wide, set it to True, initialize the fields and set the flag back to False. Here is the code:
Code
class A:
f = 90
a = None
bypass_check = False
def __init__(self, a, b, c, d1, d2, d3, d4):
# 1st workaround
self.a = a
# 2nd workaround
object.__setattr__(self, 'b', b)
# 3rd workaround
self.__setattr__('c', c, True)
# 4th workaround
self.bypass_check = True
self.d1 = d1
self.d2 = d2
self.d3 = d3
self.d4 = d4
self.bypass_check = False
def __setattr__(self, attr, value, bypass=False):
if bypass or self.bypass_check or hasattr(self, attr):
object.__setattr__(self, attr, value)
else:
# Throw some error
print('Attribute %s not found' % attr)
a = A(1, 2, 3, 4, 5, 6, 7)
a.f = 100
a.d1 = -1
a.g = 200
print(a.f, a.a, a.d1, a.d4)
Output
Attribute g not found
100 1 -1 7
I would like to iterate through a selection of class instances and set a member variable equal to a value. I can access the members value with:
for foo in range(1,4): #class members: pv1, pv2, pv3
bar[foo] ='{0}'.format(locals()['pv' + str(foo)+'.data'])
However when I try to set/mutate the values like so:
for foo in range(1,4): #class members:
'{0}'.format(locals()['pv' + str(foo)+'.data']) = bar[foo]
I obviously get the error:
SyntaxError: can't assign to function call
I have tried a few methods to get it done with no success. I am using many more instances than 3 in my actual code(about 250), but my question is hopefully clear. I have looked at several stack overflow questions, such as Automatically setting class member variables in Python -and- dynamically set an instance property / memoized attribute in python? Yet none seem to answer this question. In C++ I would just use a pointer as an intermediary. What's the Pythonic way to do this?
An attr is a valid assignment target, even if it's an attr of the result of an expression.
for foo in range(1,3):
locals()['pv' + str(foo)].data = bar[foo]
Another developer wrote a few lines about setattr(), mostly about how it should be avoided.
setattr is unnecessary unless the attribute name is dynamic.
But they didn't say why. Do you mind elaborating why you switched your answer away from setattr()?
In this case, the attr is data, which never changes, so while
for i in range(1, 3):
setattr(locals()['pv' + str(i)], 'data', bar[i])
does the same thing, setattr isn't required here. The .data = form is both good enough and typically preferred--it's faster and has clearer intent--which is why I changed it. On the other hand, if you needed to change the attr name every loop, you'd need it, e.g.
for i in range(1,3):
setattr(locals()['pv' + str(i)], 'data' + str(i), bar[i])
The above code sets attrs named data1, data2, data3, unrolled, it's equivalent to
pv1.data1 = bar[1]
pv2.data2 = bar[2]
pv3.data3 = bar[3]
I originally thought your question needed to do something like this, which is why I used setattr in the first place. Once I tested it and got it working I just posted it without noticing that the setattr was no longer required.
If the attr name changes at runtime like that (what the other developer meant by "dynamic") then you can't use the dot syntax, since you have a string object rather than a static identifier. Another reason to use setattr might be if you need a side effect in an expression. Unlike in C, assignments are statements in Python. But function calls like setattr are expressions.
Here is an example of creating a class which explicitly allows access through index or attribute calls to change internal variables. This is not generally promoted as 'good programming' though. It does not explicitly define the rules by which people should be expected to interact with the underlying variables.
the definition of __getattr__() function allows for the assignment of (object).a .
the definition of __getitem__() function allows for the assignment of
(object)['b']
class Foo(object):
def __init__(self, a=None,b=None,c=None):
self.a=a
self.b=b
self.c=c
def __getattr__(self, x):
return self.__dict__.get(x, None)
def __getitem__(self, x):
return self.__dict__[x]
print
f1 = Foo(3,2,4)
print 'f1=', f1.a, f1['b'], f1['c']
f2 = Foo(4,6,2)
print 'f2=', f2.a, f2['b'], f2['c']
f3 = Foo(3,5,7)
print 'f3=', f3.a, f3['b'], f3['c']
for x in range(1, 4):
print 'now setting f'+str(x)
locals()['f'+str(x)].a=1
locals()['f'+str(x)].b=1
locals()['f'+str(x)].c=1
print
print 'f1=', f1.a, f1['b'], f1['c']
print 'f2=', f2.a, f2['b'], f2['c']
print 'f3=', f3.a, f3['b'], f3['c']
The result is
f1= 3 2 4
f2= 4 6 2
f3= 3 5 7
now setting f1
now setting f2
now setting f3
f1= 1 1 1
f2= 1 1 1
f3= 1 1 1
I've just started learning Python recently and the first project I'm making is a text based adventure game however I've run into a problem. I need a function that makes more objects using the class Goblin that are named after a string.
def spawn(name):
title = name
exec("{0} = {1}".format('title', Goblin))
return title, 'spawn'
Essentially, another function calls this function to create another Goblin (a class) using the input name(a string) as the name of the new Goblin.
What I don't under stand though is that when I run the code(using "bill" as the argument), it gives me this error.
bill = <class '__main__.Goblin'>
^
SyntaxError: invalid syntax
Shouldn't my function be equivalent to:
bill = Goblin
When you do this:
exec("{0} = {1}".format('title', Goblin))
format method converts Goblin class by calling default __str__ method which yields <class '__main__.Goblin'>
Do this instead:
exec("{0} = {1}".format('title', 'Goblin'))
Wait! don't to this, just do:
title = Goblin
as it's strictly equivalent (without any security issues :)).
But that will just alias Goblin class to title. No real interest to all this after all (unless you want to create an instance?: title = Goblin())
With your comment: "I want a Goblin that is named after the string which title represents" I get it: you need
exec("{0} = {1}".format(title, 'Goblin()'))
(no quotes for the first arg so the name you're passing is used, and () on the second to create an instance)
Again: this is really a clumsy way of doing it. What if you want to iterate through all your goblins?
It would be much better to create a dictionary:
goblins_dict = dict()
goblins_dict["my_goblin"] = Goblin()
goblins_dict["my_goblin_2"] = Goblin()
and so on...
I was reading up on questions from a python quiz. Here is the following code and its respective question:
class Player(object):
def __init__(self, name, health):
self._name = name
self._health = health
def get_health(self):
"""Return the players health."""
## LINE ##
What is the required code for ## LINE ## so that the method satisfies the comment?
(a) print(self.health)
(b) return self.health
(c) print(self._health)
(d) return self._health
(e) More than one of the above is correct.
So, I'm wondering, is this question ambiguous?
If I state that a specific function's purpose is to "return the value of x", could that not be interpreted as both literally employing the return command to give x's value and using the print command to display the value.
Both give the same answer at face value in the interpreter.
Of course, things are different if you attempt to manipulate it indirectly:
get_health() * 5 yields a normal output if using return
get_health() * 5 yields an error if using print
So should I always treat 'return something' as actually using the return command?
I suppose print and return would both be viable only if the function's purpose said something like "Display the value in the python interpreter".
The correct answer is simply d): return self._health.
You almost answered your own question. Return in programming parlance means use of (the) return (Python/C/... statement, or an implicit return in other languages, etc).
The point here is that that the comment is meant for programmers, not users.
A print statement would imply something to the user running your program ("return output visible to the user"), but the user will not see or know about that comment.
And, as you already pointed out, the use of returning an actual value allows constructs like get_health() * 5.
Going one small step further, I would expect a printing function to be called print_health(); but that's up to the logic of the programming standard & style that is being used.