Syntax confusion with class - python

This is the given code:
class Person:
def __init__(self, name):
self.name = name
def greeting(self):
return "hi, my name is " + self.name
some_person = Person("yeabsira")
print(some_person.greeting())
However, I was expecting the syntax in which the constructor method uses like:
class Name:
def __init__(self,atribute1,atribute2):
self.atribute1=atribute1
self.atribute2=atribute2
new_instance=Name("example")
print(new_instance.atribute1)
expect answer="example"
So my question is how "some_person.greeting()" symantic works?

some_person.name returns the value of the person's name, while some_person.greeting() returns a greeting with the name, it is just a function defined within the class Person and works normally like any other function. You could use some_person.name if you only need the name.
However, by using some_person.greeting(), you implemented data hiding, which hides internal object details, and the user (in the future) only needs to access greeting() and not the value of name itself.

Related

Why am I receiving this AttributeError: 'Contact' object has no attribute 'print_entry'?

I was given the code that begins with the print_directory function and told not to alter anything below that. I've created the contact class above. Why am I receiving the attribute error, and how would I fix it? I'm trying to create a class that uses the variables in the main function to return the first name, last name, and phone numbers of the contacts list.
My code is below:
class Contact:
fname = ""
lname = ""
p_number = ""
def __init__(self,fname,lname,p_number):
self.fname = fname
self.lname = lname
self.p_number = p_number
def getfname(self):
return self.fname
def getlname(self):
return self.lname
def getp_number(self):
return self.p_number
def print_directory(contacts):
print("My Contacts")
print("-----------")
for person in contacts:
person.print_entry()
print("-----------\n")
# -----------------------------------------------------
def main():
champ = Contact("???", "Bobcat", "406-994-0000")
president = Contact("Waded", "Cruzado", "406-994-CATS")
professor = Contact("John", "Paxton", "406-994-4780")
contacts = [champ, president, professor]
print_directory(contacts)
champ.set_first_name("Champ")
president.set_title("President")
professor.set_title("Professor")
print_directory(contacts)
print("The area code for cell number", champ.get_cell_number(), "is", \
champ.get_area_code())
# -----------------------------------------------------
main()
Hi it's seem that you trying to call a method that not exist for the "Contact" class.
person.print_entry()
the class Contact does not know to do with that call.
maybe you have to implement an print_entry() method in the class:
class Contact:
fname = ""
lname = ""
p_number = ""
def __init__(self,fname,lname,p_number):
self.fname = fname
self.lname = lname
self.p_number = p_number
def getfname(self):
return self.fname
def getlname(self):
return self.lname
def getp_number(self):
return self.p_number
def print_entry(self):
print(f" Name: {self.lname}, {self.fname}, Number: {self.p_number}")
Your code will throw AttributeError: 'Contact' object has no attribute 'print_entry' because in your Contact class the method print_entry isn't defined.
Additionally you will also need to define set_first_name, and set_title in your Contact class or any lines where you call these class methods will throw a similar AttributeError.
You can add a definition for print_entry like this:
class Contact:
...
def print_entry(self):
# add your desired print statement here
...
Then use a similar structure to add your set_first_name and set_title methods. Something like this:
class Contact:
...
def set_first_name(self, fname):
self.fname = fname
...
However it is unclear which instance variable you want set_title to modify. You might consider adding another instance variable called title to your class.
TL;DR: Define a method print_entry(self) that uses print to output as expected (this solution was already answered).
A more idiomatic, object-oriented and maintainable solution would be to use __str__ and print in two different levels (see below).
Background
Attribute Error explained
This is always a signal that your class or a third-party class is missing some attributes that you referenced, like accessed fields or invoked methods.
So you need to define it or if a third-party module used via import, you should check version-mismatches, etc.
Printing an object's representation
In almost every object-oriented programming-language there is a method that converts the object instance to a string representation. In Python this is either __repr__(self) or __str__(self) (similar like toString() in Java). These are standard methods (recognize by the surrounding double underscores __) that already exist and usually can be overridden.
Solution
Instead calling and defining print_entry instance-method, override the __str__ method like:
class Contact:
# omitted other to focus on string method
def __str__(self):
return "Name: " + self.last_name + ", " + self.first_name;
and use it to print a Contact object instance person like:
print(person)
See also:
How to print instances of a class using print()?
Benefits:
this is an object-oriented pattern, reusing Python's standard
keeping all print statements in one place (principle: abstraction-level, code-symmetry) like in your method print_directory
string-representation (__str__) separated from UI/output-layer (print) so that they can be changed independently (principle: separation of concerns & single-responsibility)
Bonus
Tips:
use an IDE like PyCharm (free community edition) or Eric, that will warn you in advance and offers quick-fix actions to add the missing attributes (methods, etc.).
python naming convention for fields and methods is both lower snake-case, e.g. first_name
setters need to be defined too, i.e. set_first_name(self), set_title(self)
getters used for get_cell_number and get_area_code are missing in class (whereas there is a field p_number with getter
meaningful names instead abbreviations usually pay-off in the long-run: phone_number can be distinguished from person_number (some IDEs offer dictionary-assisted auto-completion and will pass spellchecking this way)
a pythonic way for string-interpolation using templates (since Python 3) are f-strings (see applied in Ulises answer)

Accessing undefined variable outside of method

I don't understand why this code works, I think it should give an error but it doesn't:
class Person:
def __init__(self, name):
self.name = name
def greeting(self):
# Should return "hi, my name is " followed by the name of the Person.
return "hi, my name is "+self.name
def __str__(self):
return "str"
some_person = Person("foo")
print(print(some_person.greeting()))
In the __init__ method, I try to access a variable that is not actually defined (shouldn't be an error?)
and in the greeting method, I try to access that variable which is just defined in __init__ (shouldn't it be an error?). It doesn't give any error and works smoothly? how is that possible?
def __init__(self, name):
self.name =name
In fact, you tried to access a variable that is actually defined. name is defined in function parameter.
It shouldnt be an error! That's because self is the Object itself (you see why its called self?) so if you define something in the __init__ function, it is stored inside the object so you can use it from another function by peeling it off the object itself. The name variable you were talking about is defined in the function definition! Where you put __init__(self,name)

Call function of class using a variable function call

I'm trying to call a function from a Class thats name will change depending on what type of enemy is being instantiated. How can I accomplish this?
My attempt was this: AssignClass.[self.Class](self)
but obviously that sintax makes no sense
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
# Using a variable function call to remove
# the need for a ton of 'if' statements
AssignClass.[self.Class](self)
# Basically automate doing this:
if self.Class = "Orc":
AssignClass.Orc(self)
elif self.Class = "Wizard"
AssignClass.Wizard(self)
class AssignClass:
def Orc(Creature):
Creature.Class='Orc'
Creature.Health=100
Creature.Mana=0
def Wizard(Creature):
Creature.Class='Wizard'
Creature.Health=75
Creature.Mana=200
Evil_Wizard = Creature("Evil Wizard", "Wizard")
You can retrieve class methods using getattr() and then just pass your Creature instance, e.g.:
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
getattr(AssignClass, Class)(self)
Btw. this is everything but a recommended style for Python classes, the least of which is that you're shadowing the Creature class in your AssignClass (which shouldn't be a class in the first place). I'd recommend you to read the PEP 8 -- Style Guide for Python Code thoroughly.
Played around a little more and found that I can use eval for this. (Safe as no user input can be added here)
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
eval('AssignClass.'+Class)(self)

Python OOP: Are all self."something" attributes in __init__ available to other class methods?

Simple, silly question.
But say I had
class Stuff:
def __init__(self, name):
self.name = name:
def get_name(self):
print(name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Would this work? Would get_name be able to access the name attribute and print it out?
I can't get this code to work...
There are a few things that you need to change but this works:
class Stuff:
def __init__(self, name):
self.name = name
def get_name(self):
print(self.name)
new_name = Stuff(name = "Richard")
new_name.get_name()
Besides a few syntax errors (class needs to be lowercase and some missing :) the main thing you were missing was accessing name by means of the self identifier. Since name is defined on the class you need to access it via self.

How remove a variable that appears in __init__?

If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.

Categories

Resources