How to restrict the value of sex argument here to either male or female?
class SoftwareEngineer:
def __init__(self, name, age, sex, level, salary):
sexes=['male','female']
if sex in sexes:
self.sex=sex.lower()
self.name=name
self.age=age
self.level=level
self.salary=salary
Second attempt:
class SoftwareEngineer:
def __init__(self, name, age, sex=['male','female'], level, salary):
sexes=['male','female']
if sex in sexes:
self.sex=sex.lower()
self.name=name
self.age=age
self.level=level
self.salary=salary
The simplest approach would be to validate your parameters before creating a SoftwareEngineer object - by the time __init__ is called the object has already been created. But, if you want to do your parameter validation in the initializer then you could do something like the code below to kick it back out to the surrounding code to handle.
Example:
class SoftwareEngineer:
def __init__(self, name, age, sex, level, salary):
self._validate_sex(sex)
self.sex = sex.lower()
self.name=name
self.age=age
self.level=level
self.salary=salary
def _validate_sex(self, sex):
if sex.lower() not in ["male", "female"]:
raise ValueError(f"Person's sex must specified as male or female not {sex}.")
parameters_validated = False
while not parameters_validated:
# Other parameter inputs not shown for simplicity
sex = input("Please enter sex of person [male, female]: ")
try:
swe = SoftwareEngineer("Fiona", 25, sex, 6, 12000)
except ValueError as exc:
print(exc)
print("Please try again.")
else:
print("All good, thanks.")
parameters_validated = True
Traditionally you’d enforce this by raising a ValueError, which is defined as:
Raised when an operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.
class SoftwareEngineer:
def __init__(self, name, age, sex, level, salary):
sexes={'male','female'}
sex = sex.lower()
if sex in sexes:
self.sex = sex
else:
raise ValueError(“sex must be ‘male’ or ‘female’.”)
(Side note: I have friends who are software engineers who would be pretty annoyed at this arbitrary distinction. If you have to do this for homework, so be it. If you’re doing it for real, consider just storing whatever value gets passed in.)
Related
I have multiple classes and I have instances from each class e.g: Student class. every instance (a student) has their own courses. Now when a user signs in (by input) I want to print their list of courses. Or even just their age to show that I have the correct object.
Is there a better way than eval() to get an object from class based on input
like the following example:
class Student:
def __init__(self, name, age):
self._name = name
self._age = age
blablue = Student('bla blue', '23')
name = input('enter your name')
name = name.split(' ')
stundent = eval(name[0] + name[1])
print(student)
print(student.age)
output:
enter your name: bla blue
<__main__.Foo object at 0x000001B2978C73C8>
23
I assume this is for educational purpose (production code would use a SQL database and some ORM):
try:
# python 2.x
input = raw_input
except NameError:
# python 3.x
pass
class AlreadyExists(ValueError):
pass
class DoesNotExist(LookupError):
pass
class FooCollection(object):
def __init__(self):
self._foos = {}
def add(self, foo):
if foo.name in self._foos:
raise AlreadyExists("Foo with name '{}' already exists".format(foo.name))
self.update(foo)
def update(self, foo):
self._foos[foo.name] = foo
def get(self, name):
try:
return self._foos[name]
except KeyError:
raise DoesNotExist("no Foo named '{}'".format(name))
class Foo(object):
def __init__(self, name, age):
self._name = name
self._age = age
# we at least need to be able to read the name
#property
def name(self):
return self._name
def __repr__(self):
return "Foo({}, {})".format(self._name, self._age)
def main():
foos = FooCollection()
blablue = Foo('bla blue', '23')
foos.add(blablue)
name = input('enter your name: ').strip()
try:
print("found {}".format(foos.get(name)))
except DoesNotExist as e:
print(e)
if ___name__ == "__main__":
main()
The principle here is to have a storage for your instances. I chose a dict for fast lookup with the Foo.name as key, in real life you'd probably want an opaque unique identifier for each instance and multiple indexes (i.e. one by id, one by name etc) - but actually in real life you would use a SQL database that already provide all those features in a much more optimized way ;-)
Also, I wrapped the dict in a dedicated class with its own interface. This allows to decouple the interface from the implementation (if you later decide you want more indexes than just name for example), and encapsulate domain logic too (i.e. checking you don't accidentally overwrite an existing Foo).
Pylint tells me that I set _age outside of __init__ which is not good stylistically and I see why. However, if I use properties to ensure that my attributes are set within a certain interval, then it does make sense to have the attributes set in the property setter. How do I reconcile these two opposing thoughts?
class Person:
def __init__(self, age, height, weight):
self.age = age
#property
def age(self):
return self._age
#age.setter
def age(self, age):
if 18 <= age <= 81:
self._age = age
else:
raise ValueError('You are either too old or too young')
You are not really implementing the getters/setters properly. What you should be doing in your init is actually setting self._age = age:
def __init__(self, age, height, weight):
self._age = age
With that correction, now things will work as expected according to your design:
p = Person(1, 2, 3)
p.age = 10
Output:
ValueError: You are either too old or too young
Non exception:
p = Person(1, 20, 3)
p.age = 22
age = p.age
print(age)
Output: 22
It's clear that the answer given by idjaw misunderstands the design of the code, skipping the exception that should be raised when he tries to set the 'age' to 1.
The pylint exception appears to be a known issue that has just never been resolved. Your best bet would just be to use # pylint: disable=attribute-defined-outside-init in-line.
I have this code that stores a person's name, age and funds. I am trying to write a method that can take "age" or "funds", along with a number, and increase the given attribute by that number.
class Actor:
def __init__(self, name, age, funds):
self.name
self.age = age
self.funds = funds
def increase_age(self, increase_amount=1):
self.age = self.age + increase_amount
def increase_attrib(self, attrib, increase_amount=1):
self.attrib = self.attrib + increase_amount
a = Actor("Andrea", 32, 10000)
a.increase_age() works fine: calling it increases the age of Andrea to 33, just as expected. However, a.increase_attrib("age") gives an error, saying AttributeError: 'Actor' object has no attribute 'attrib'. a.increase_attrib("funds") gives similar results.
If I just say a.increase_attrib(age) (without the quotes) I get a NameError, which is what I expected.
By my understanding, giving the parameter "age" to increase_attrib() should mean that the attrib mentioned becomes age, so that increase_attrib() references self.age instead of self.attrib. Apparently, I was wrong.
Now, I could just use increase_age() instead, but then I'd have to make different methods for age and funds, and even more methods once I add other features to Actor, like location, gender, and nationality.
What do I need to do so that I can pass the name of an attribute to a method and have it change that attribute?
You are looking for setattr:
setattr(obj, 'foo', 42)
Is the same as
obj.foo = 42
So for your example:
def increase_attrib(self, attrib, increase_amount=1):
setattr(self, attrib, getattr(self, attrib, 0) + increase_amount)
I'm attempting utilizing TeamTreehouse learning subscription & this Starting Out With Programming Logic And Design (3rd Ed) book to attempt learning programming & Python. Please don't shoot to kill me I'm learning.
Goal: I'm attempting to set a Pet class with 3 fields/attributes/properties. The class will have 2 methods, set & get, for each field/attribute/property. I'm trying to figure out how to get each method (which is still a function at it's roots) to ask user's for input to then set the internal attributes to the input values.
Researching & Troubleshooting: I can get it to run error free when I don't ask users to input data as part of class methods. http://repl.it/oIr/3
I've referred to a "cheat sheet" for what OOP could look like. http://www.newthinktank.com/2014/11/python-programming/
Here is a doc that I flipped through that appears to be a 2.x python version reference on OOP though it didn't do much for relieving or causing a change on my headache.
I found this document right before posting which is starting to make sense of my cloudy head. Likely I need to clean/scrub all my passing by value/reference coding then rewrite?
How to get user input within a Method (python)
Error:
Traceback (most recent call last):
File "python", line 43, in <module>
TypeError: __init__() takes 0 positional arguments but 1 was given
CODE:
http://repl.it/oIr/5
# Create class for Pet
class Pet:
__name = ""
__species = ""
__age = ""
# Object constructor
def __init__():
self.__name = name
self.__species = species
self.__age = age
#Methods
def setName(self):
self.__name =input("What is your pet's name?\n")
def setSpecies(self, species):
self.__species=input("What type of pet is it?\n")
def setAge(self, age):
self.__age=input("How old is your pet?\n")
def getName(self):
return self.__name
def getSpecies(self):
return self.__species
def getAge(self):
return self.__age
def get_type(self):
print("Pet")
def toString(self):
return"{} is a {} and is {} years old".format(self.__name,self.__species,self.__age)
#name=input("What is your pet's name?\n")
#species=input("What type of pet is it?\n")
#age=input("How old is your pet?\n")
myPet=Pet()
myPet.setName()
#myPet=Pet(name,species,age)
print(myPet.toString())
All methods in Python take a self argument. Your __init__ method doesn't:
def __init__():
but Python is still trying to supply one:
TypeError: __init__() takes 0 positional arguments but 1 was given
Add the self argument:
def __init__(self):
Your next problem is that your __init__ method expects the variables name, species and age to be available, but you never set those:
def __init__(self):
self.__name = name
self.__species = species
self.__age = age
Perhaps you meant to make those method arguments:
def __init__(self, name, species, age):
self.__name = name
self.__species = species
self.__age = age
which means you can only create a new Pet() instance by including those values:
name = input("What is your pet's name?\n")
species = input("What type of pet is it?\n")
age = input("How old is your pet?\n")
myPet = Pet(name, species, age)
I have written some python code:
class key(object):
def __init__(self,name):
object.__init__(self,age)
this.name = name
this.age = age
def somefunction(self):
print "yay the name is %d" % self.name
baby = key('radan',20)
baby.somefunction()
When I create an instance of key with baby = key('radan',20), I got a TypeError. I don't know why I am getting this error. Is it because of object.__init__(self,age)?
If yes, please help me in explaining why we use object.__init__(self,age) and what the purpose of that is and help me solve this code.
Some pointers:
class Key(object): # in Python, classes are usually named with a starting capital
def __init__(self, name, age): # name all your arguments beside self
self.name = name # use self.name, not this.name
self.age = age
def somefunction(self):
print "yay the name is %s" % self.name
baby = Key('radan',20)
baby.somefunction()
# output: yay the name is radan
Actually, you can can name the self instance parameter whatever you like in Python (even this), but it makes the code harder to read for other people, so just use self.
You don't have to use object.__init__(self, name, age) here. If you remove that line and implement the changes above, your code will work just fine.
Your code contains several errors:
class key(object):
def __init__(self, name, age): # where's your age?
self.name = name # no need to call object.__init__
self.age = age # there is no such thing called "this", use self
def somefunction(self):
print "yay the name is %s" % self.name # use %s as the comment says
baby = key('radan', 20)
baby.somefunction()
output:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan
When you do baby = key('radar', 20) you are actually passing three arguments: the instance, the name and the age. However your initialiser is defined to take exactly two arguments so you get a TypeError.
self is the argument implicitly passed when referring to an instance of an object.
For your __init__ function, I would just do:
def __init__(self, name, age):
self.name = name
self.age = age
So we can assign the arguments passed as attributes to the current instance, conventionally called self.
It makes no sense here to call object.__init__ at all, just remove that line.
Apart from that, everything works fine (except use %s instead of %d).
Testing:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan