python __getattr__ help - python

Reading a Book, i came across this code...
# module person.py
class Person:
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def lastName(self):
return self.name.split()[-1]
def giveRaise(self, percent):
self.pay = int(self.pay *(1 + percent))
def __str__(self):
return "[Person: %s, %s]" % (self.name,self.pay)
class Manager():
def __init__(self, name, pay):
self.person = Person(name, "mgr", pay)
def giveRaise(self, percent, bonus=.10):
self.person.giveRaise(percent + bonus)
def __getattr__(self, attr):
return getattr(self.person, attr)
def __str__(self):
return str(self.person)
It does what I want it to do, but i do not understand the __getattr__ function in the Manager class. I know that it Delegates all other attributes from Person class. but I do not understand the way it works. for example why from Person class? as I do not explicitly tell it to. person(module is different than Person(class)
Any help is highly appreciated :)

In your __init__ you instantiate a Person object which gets assigned to self.person.
You then override attribute lookups on the Manager instance (by implementing __getattr__ for this class) and redirect these attributes to be looked up on the self.person variable instead (which is the Person object from 1 in this particular case).
Like Felix Kling mentioned in the comments, it would make more sense to make Manager inherit from Person. In the current code above, it looks like the manager has a person while it's more logical to think that the manager is a person.
You could do something like this:
class Person(object):
def __init__(self, name, job=None, pay=0):
self.name = name
self.job = job
self.pay = pay
def give_raise(self, percent):
self.pay = int(self.pay *(1 + percent))
def __str__(self):
return "[Person: %s, %s]" % (self.name, self.pay)
class Manager(Person):
def __init__(self, name, pay):
super(Manager, self).__init__(name, "mgr", pay)
def give_raise(self, percent, bonus=.10):
self.pay = int(self.pay * (1 + (percent + bonus)))
# example usage
John = Person("John", "programmer", 1000)
Dave = Manager("Dave", 2000)
print John, Dave
John.give_raise(.20)
Dave.give_raise(.20)
print John, Dave

Actually, you do tell it explicitly - not by naming the class, but by providing an instance of that class.
In the init method, you bind self.person to an instance of Person. Now, every Manager instance will have this data member.
In __getattr__, you are delegating to the getattr builtin with self.person as the first argument. Regardless of the type of self.person, it will look for a member with the given name.

Beside reading a Book, you might want to consult The Book where you could have found a pretty clear explanation of how __getattr__() methods work.
In a nutshell, it gets called when there are no attributes of the specified name attached to the object it's being applied to, and also not to the object class or any of it's superclasses. In other words, it called when all else fails.
In the code in your example, the implementation of __getattr_() effectively redirects the search for the named attribute onto the self.person object, which is an instance of the Person class.
It also important to understand that __getattr_() is the first step in accessing both the data and the methods associated with any object.

Related

Python3 Class method inputs; clean solution

I'am using more class based programs, however in some cases it's not handy to provide all self.paramets into a class.
In those cases I want to use a regular input into a function in a class. I figured out a way to achieve both inputs, let me show this in following script:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(a):
if (type(a) == str):
name = a
else:
name = a.name
print("Hello my name is " + name)
p1 = Person("John", 36)
p1.myfunc()
print("---------------------")
Person.myfunc("Harry")
Output:
Hello my name is John
---------------------
Hello my name is Harry
First, the name is initialized by the classes self.params.
Second, the name is provided in the method within the class as a string.
So a type check is necessary.
However I don't think this is a clean approach, because when I have >30 methods I need to implement these type checks again, including upcoming type-error results.
Does anyone know a better approach?
The simplest solution is to implement a __str__ method for your class. This method will be called whenever something tries to convert an instance of the class to a string.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('Jane', 25)
print('Hello', p)
'Hello Jane'

"Cannot create a consistent method resolution order" inheriting from another child class

I am revising OOP in Python and tried inheriting attributes from another child class but I couldn't figure out how or if it is possible. Here is what I have so far:
class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
def increase_pay(self, multiplier):
self.pay = int(self.pay * multiplier)
class Developer(Employee):
def __init__(self, first, last, pay, prog_lang):
Employee.__init__(self, first, last, pay)
self.prog_lang = prog_lang
self.email = first.lower() + '.' + last.lower() + '#outlook.com'
class BetaTester(Employee, Developer):
def __init__(self, first, last, pay, prog_lang, platform):
self.platform = platform
The error I receive is:
Traceback (most recent call last):
File "main.py", line 33, in <module>
class BetaTester(Employee, Developer):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Employee, Developer
The method resolution order (MRO) is defined by the C3 linearization algorithm, which sounds complicated but it really boils down to this: the class, its parents, their parents, etc need to be placed in a list subject to two conditions:
Each class appears before its parent(s)
If a class inherits from more than one class, its parents appear in the same
order as they do in class statement. That is, given class A(B, C, D), the MRO for A will have B before C, which will be before D. (A, of course, appears before all 3)
You should be able to see the problem: by this algorithm, the MRO for BetaTester has to include Developer before Employer according to the first rule, but Employer has to come before Developer according to the second rule. In this case, you can simply swap the two to fix the problem, but there's never any reason to inherit from a class A and another class the inherits from A. Just drop A altogether.
# Developer is already a descendent of Employee, so BetaTester will be, too
class BetaTester(Developer):
...
To make sure each class's __init__ method is called, use super to make sure each __init__ calls the next one in the chain. The most important rule here is to make sure that if a class adds arguments to __init__, it has to make sure not to pass them on to the next __init__. At the same time, it has to accept arbitrary keywords arguments and be sure to pass them on. Keyword arguments make it simpler to focus on the arguments you need to deal with, and just pass on the ones you don't.
class Employee:
def __init__(self, first, last, pay, **kwargs):
super().__init__(**kwargs)
self.first = first
self.last = last
self.pay = pay
def increase_pay(self, multiplier):
self.pay = int(self.pay * multiplier)
class Developer(Employee):
def __init__(self, prog_lang, **kwargs):
super().__init__(**kwargs)
self.prog_lang = prog_lang
self.email = "{}.{}#outlook.com".format(self.first.lower(), self.last.lower())
class BetaTester(Developer):
def __init__(self, platform, **kwargs):
super().__init__(**kwargs)
self.platform = platform
b = BetaTester(first="Bob", last="Jones", pay=90000, prog_lang="Python", platform="Unix")
#MehrdadEP has answered very well
I think i should make it easy
suppose there are 2 classes A and B
B inherits from A
Now you are creating a new class C
if you inherit from B you are already inheriting from A. no need to write that in class C(A,B)
observe C(B,A) will not give you error
But C(A,B) will
Another thing is you should use Super().__init__() instead of Employee.__init__()
This may not be handy when doing Hybrid Inheritance due to more than 1 different super classes where you would have to call classname1.__init__() , classname2.__init__() and so on
also make sure if you want the attributes defined in SuperClass then call the classname.__init__() so that they are defined in new class's scope otherwise
you will get an error
for example
print(b1_1.first) will give an error
to resolve that use Developer.__init__(self, first, last, pay, prog_lang)
and don't forget the self in this Developer.__init__ calling from subclass __init__
use super() in __init__ method of sub classes
Your BetaTester is inheriting from Employee and Developer. Because Developer already inherits from Employee Python now cannot determine what class to look methods up on first. You don't need to name all base classes of Developer here; just inherit from that one class.
here is your fixed code:
class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
def increase_pay(self, multiplier):
self.pay = int(self.pay * multiplier)
emp_1 = Employee('David', 'Jackson', 35000)
print (emp_1.pay)
emp_1.increase_pay(1.2)
print (emp_1.pay)
class Developer(Employee):
def __init__(self, first, last, pay, prog_lang):
super().__init__(first, last, pay)
self.prog_lang = prog_lang
self.email = first.lower() + '.' + last.lower() + '#outlook.com'
dev_1 = Developer('James', 'McCarthy', 70000, 'C++',)
print(dev_1.first)
print(dev_1.email)
class BetaTester(Developer):
def __init__(self,first, last, pay, prog_lang, platform):
self.platform = platform
bt_1 = BetaTester('Jonas', 'Andersen', 45000, 'C#', 'Mobile')
print(bt_1.platform)

Variables starting with underscore for property decorator

I am new to Python. So, please forgive me if this is a basic question. I researched this topic on the Internet and SO, but I couldn't find an explanation. I am using Anaconda 3.6 distribution.
I am trying to create a simple getter and setter for an attribute. I will walk you through the errors I get.
class Person:
def __init__(self,name):
self.name=name
bob = Person('Bob Smith')
print(bob.name)
This prints the first name I agree that I haven't overridden print or getattribute method. Also, there is no property here. This was to test whether the basic code works.
Let's modify the code to add property:
class Person:
def __init__(self,name):
self.name=name
#property
def name(self):
"name property docs"
print('fetch...')
return self.name
bob = Person('Bob Smith')
print(bob.name)
As soon as I write above code in PyCharm, I get a yellow bulb icon, stating that the variable must be private. I don't understand the rationale.
Ignoring above, if I run above code, I get:
Traceback (most recent call last): File "C:\..., in run_code
exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-25-62e9a426d2a9>", line 2, in <module>
bob = Person('Bob Smith') File "<ipython-input-24-6c55f4b7326f>", line 4, in __init__
self.name=name AttributeError: can't set attribute
Now, I researched this topic, and I found that there are two fixes (without knowing why this works):
Fix #1: Change the variable name to _name
class Person:
def __init__(self,name):
self._name=name #Changed name to _name
#property
def name(self):
"name property docs"
print('fetch...')
return self._name #Changed name to _name
bob = Person('Bob Smith')
print(bob.name)
This works well in that it prints the output correctly.
Fix #2: Change property name to from name(self) to _name(self) and revert variable name from _name to name
class Person:
def __init__(self,name):
self.name=name #changed to name
#property
def _name(self): #Changed to _name
"name property docs"
print('fetch...')
return self.name #changed to name
bob = Person('Bob Smith')
print(bob.name)
Now, this works prints as expected.
As a next step, I created setter, getter, and deleter properties using decorators. They follow similar naming conventions as described above--i.e. either prefix _ to the variable name or the method name:
#_name.setter
def _name(self,value):
"name property setter"
print('change...')
self.name=value
#_name.deleter
def _name(self):
print('remove')
del self.name
bob = Person('Bob Smith')
print(bob.name)
bob.name = 'Bobby Smith'
print(bob.name)
del bob.name
Question: I am not really sure why Python 3.x is enforcing adding _ to variable name or method name.
As per Python property with public getter and private setter, What is the difference in python attributes with underscore in front and back, and https://www.python.org/dev/peps/pep-0008/#naming-conventions, an underscore prefix is a weak indicator to the user that this variable is a private variable, but there is no extra mechanism in place (by Python, similar to what Java does) to check or correct such behavior.
So, the big question at hand is that why is it that I need to have underscores for working with properties? I believe those underscore prefixes are just for users to know that this is a private variables.
I am using Lutz's book to learn Python, and above example is inspired from his book.
Lets take your code Fix 1:
class Person:
def __init__(self,name):
self._name=name #Changed name to _name
#property
def name(self):
"name property docs"
print('fetch...')
return self._name #Changed name to _name
bob = Person('Bob Smith')
print(bob.name)
You define self._name = name - thats your backing field.
You define a method def name(self) - and attribute it with #property.
You create an instance of your class by bob = Person('Bob Smith')
Then you do print(bob.name) - what are you calling here?
Your variable is called self._name - and a "non-property" method would be called by bob.name() .. why does bob.name still work - its done by the #property decorator.
What happens if you define:
def tata(self):
print(self.name) # also no () after self.name
bob = Person('Bob Smith')
bob.tata()
It will also call your #property method as you can inspect by your 'fetch...' output. So each call of yourclassinstance.name will go through the #property accessor - thats why you can not have a self.name "variable" together with it.
If you access self.name from inside def name(self) - you get a circular call - hence: stack overflow.
This is pure observation - if you want to see what exactly happens, you would have to inspect the #property implementation.
You can get more insight into the topics here:
How do Python properties work?
How does the #property decorator work in Python?
What's the pythonic way to use getters and setters?
As pointed out in the comment, using getters/setters is an anti-pattern unless they actually do something:
class Person:
"""Silly example for properties and setter/deleter that do something."""
def __init__(self,name):
self._name = name # bypass name setter by directly setting it
self._name_access_counter = 0
self._name_change_counter = 0
self._name_history = [name]
#property
def name(self):
"""Counts any access and returns name + count"""
self._name_access_counter += 1
return f'{self._name} ({self._name_access_counter})'
#name.setter
def name(self, value):
"""Allow only 3 name changes, and enforce names to be CAPITALs"""
if value == self._name:
return
new_value = str(value).upper()
if self._name_change_counter < 3:
self._name_change_counter += 1
print(f'({self._name_change_counter}/3 changes: {self._name} => {new_value}')
self._name_history.append(new_value)
self._name = new_value
else:
print(f"no change allowed: {self._name} => {new_value} not set!")
#name.deleter
def name(self):
"""Misuse of del - resets counters/history for example purposes"""
self._name_access_counter = 0
self._name_change_counter = 0
self._name_history = self._name_history[:1] # keep initial name
self._name = self._name_history[0] # reset to initial name
print("deleted history and reset changes")
#property
def history(self):
return self._name_history
Usage:
p = Person("Maria")
print(list(p.name for _ in range(5)))
for name in ["Luigi", "Mario", 42, "King"]:
p.name = name
print(p.name) # counter will count ANY get access
print(p.history)
del (p.name)
print(p.name)
print(p.history)
Output:
# get 5 times and print as list
['Maria (1)', 'Maria (2)', 'Maria (3)', 'Maria (4)', 'Maria (5)']
# try to change 4 times
(1/3 changes: Maria => LUIGI
LUIGI (6)
(2/3 changes: LUIGI => MARIO
MARIO (7)
(3/3 changes: MARIO => 42
42 (8)
no change allowed: 42 => KING not set!
42 (9)
# print history so far
['Maria', 'LUIGI', 'MARIO', 'KING']
# delete name, print name and history after delete
deleted history and reset changes
Maria (1)
['Maria']

Python: showing attributes assigned to a class object in the class code

One of my classes does a lot of aggregate calculating on a collection of objects, then assigns an attribute and value appropriate to the specific object: I.e.
class Team(object):
def __init__(self, name): # updated for typo in code, added self
self.name = name
class LeagueDetails(object):
def __init__(self): # added for clarity, corrected another typo
self.team_list = [Team('name'), ...]
self.calculate_league_standings() # added for clarity
def calculate_league_standings(self):
# calculate standings as a team_place_dict
for team in self.team_list:
team.place = team_place_dict[team.name] # a new team attribute
I know, as long as the calculate_league_standings has been run, every team has team.place. What I would like to be able to do is to scan the code for class Team(object) and read all the attributes, both created by class methods and also created by external methods which operate on class objects. I am getting a little sick of typing for p in dir(team): print p just to see what the attribute names are. I could define a bunch of blank attributes in the Team __init__. E.g.
class Team(object):
def __init__(self, name): # updated for typo in code, added self
self.name = name
self.place = None # dummy attribute, but recognizable when the code is scanned
It seems redundant to have calculate_league_standings return team._place and then add
#property
def place(self): return self._place
I know I could comment a list of attributes at the top class Team, which is the obvious solution, but I feel like there has to be a best practice here, something pythonic and elegant here.
If I half understand your question, you want to keep track of which attributes of an instance have been added after initialization. If this is the case, you could use something like this:
#! /usr/bin/python3.2
def trackable (cls):
cls._tracked = {}
oSetter = cls.__setattr__
def setter (self, k, v):
try: self.initialized
except: return oSetter (self, k, v)
try: self.k
except:
if not self in self.__class__._tracked:
self.__class__._tracked [self] = []
self.__class__._tracked [self].append (k)
return oSetter (self, k, v)
cls.__setattr__ = setter
oInit = cls.__init__
def init (self, *args, **kwargs):
o = oInit (self, *args, **kwargs)
self.initialized = 42
return o
cls.__init__ = init
oGetter = cls.__getattribute__
def getter (self, k):
if k == 'tracked': return self.__class__._tracked [self]
return oGetter (self, k)
cls.__getattribute__ = getter
return cls
#trackable
class Team:
def __init__ (self, name, region):
self.name = name
self.region = region
#set name and region during initialization
t = Team ('A', 'EU')
#set rank and ELO outside (hence trackable)
#in your "aggregate" functions
t.rank = 4 # a new team attribute
t.ELO = 14 # a new team attribute
#see witch attributes have been created after initialization
print (t.tracked)
If I did not understand the question, please do specify which part I got wrong.
Due to Python's dynamic nature, I don't believe there is a general answer to your question. An attribute of an instance can be set in many ways, including pure assignment, setattr(), and writes to __dict__ . Writing a tool to statically analyze Python code and correctly determine all possible attributes of an class by analyzing all these methods would be very difficult.
In your specific case, as the programmer you know that class Team will have a place attribute in many instances, so you can decide to be explicit and write its constructor like so:
class Team(object):
def __init__(name ,place=None):
self.name = name
self.place = place
I would say there is no need to define a property of a simple attribute, unless you wanted side effects or derivations to happen at read or write time.

TypeError in Python 3.x

I have no idea what is wrong! This is a very simple program and I have done a lot head banging! Please someone enlighten me!
This a lab problem from the CSE 111 - Programming Language II course. They teach Java at the university and the code I wrote in Java works fine.
I just have to create a Student class with some fields to hold the basic information about a student with methods to get and set the attributes. Then create an instance of that class and tryout the methods.
But every time I run this program the following error occurs:
TypeError: set_name() takes exactly 1 positional argument (2 given)
Here is the code I wrote.
class Student:
'''Student class'''
name = None
id = 0
address = None
cgpa = None
def get_name():
return name
def set_name(n):
name = n
def get_id():
return id
def set_id(i):
id = i
def get_address():
return address
def set_address(a):
address = a
def get_cgpa():
return cgpa
def set_cgpa(c):
cgpa = c
#An object of Student class
jack = Student()
jack.set_name('jacky')
print(jack.get_name())
You're not accepting a reference to your instance as the first argument to that method, i.e. your set_name() should be written:
def set_name(self, n):
self.name = n
This is somewhat different from other languages where there is a built-in keyword (such as this) that refers to the current object. Python passes that reference explicitly, as an argument to the method.
All your other methods must be modified similarly.
Note that just setting name = n sets a local variable name which goes away when the method ends; it does not set anything on the instance. You have to explicitly set self.name if you want an instance attribute.
Also, and this is a matter of style, but you do not usually write set and get methods in Python. It is normal practice to set and get attributes directly. If you want to do validation of values, use a property instead. So basically, none of your methods are actually necessary in good style.
However, you don't have an __init__() method. Usually you would pass the desired attributes of the instance when instantiating the class and save these on the instance.
class Student:
def __init__(self, name, id, address, cgpa):
self.name = name
self.id = id
self.address = address
self.cgpa = cgpa
herman = Student("Herman Munster", 12345, "1313 Mockingbird Lane", 4.0)
Try this:
import sys
class Student:
'''Student class'''
self.name = None
self.id = 0
self.address = None
self.cgpa = None
def get_name(self):
return self.name
def set_name(self, n):
self.name = n
def get_id(self):
return self.id
def set_id(self, i):
self.id = i
def get_address(self):
return self.address
def set_address(self, a):
self.address = a
def get_cgpa(self):
return self.cgpa
def set_cgpa(self, c):
self.cgpa = c
You need to pass self as the first argument to each member function of the class. Member variables must then be referred to with self, i.e. self.name. Furthermore, you may wish to include an __init__() function; this serves usually to initialize any member variables, and is called at the instantiation of the class.
Take a look at the Python documentation here for some examples on well-formed classes: http://docs.python.org/tutorial/classes.html#random-remarks
In Python, you need to pass in self for each of your member functions. You also need to reference class variables as self.x, if you want them to take an effect.
Here are a couple examples that you need to apply to the rest of your code.
def set_name(self, n):
self.name = n
def get_cgpa(self):
return self.cgpa
There is some explanation for why this is the case in the documentation.
This is because first argument of methods is self - the class instance.
See What is the purpose of self?
and http://docs.python.org/tutorial/classes.html#class-objects

Categories

Resources