I have the following class structures:
class Parent(object):
def __init__(self, id, name):
self.id = id
self.name = name
def print_vars(self):
print(vars(self))
class Child(Parent):
def __init__(self, id, name, last_name, age):
Parent.__init__(self, id, name)
self.last_name = last_name
self.age = age
class AnotherChild(Parent):
def __init__(self, id, name, address):
Parent.__init__(self, id, name)
self.address= address
Perhaps not the best example, but I hope enough to get the idea accross. The idea is that I initialise two seperate instances that share some common attributes and methods. I need to be able to dump both objects into some json and csv files which I hope to achieve with the method dump_to_file (replaced by print_vars in this example). Now this method worked fine when I had to dump all the attributes from both the parent and child classes to a single file. Howeer, I want to only dump the attributes from the parent class. I tried to replace self by super or super(Parent, self) but without much success. What is the best way to access only the attributes from the class the code is written in?
I believe Java would do this automatically as the method is defined in the parent class, and the parent class does not know the child class attributes.
Assuming that you aren't going to add more variables outside __init__, you could freeze the list of variables in the __init__ method of the parent:
def __init__(self, id, name):
self.id = id
self.name = name
self.__parent_vars = dict(vars(self)) # make a copy
then use this dictionary, which has only the variables you defined when you initialized the parent class:
def print_values(self, path):
print(self.__parent_vars)
testing:
c = Child(12,"Foo","whatever",34)
c.print_vars()
I get:
{'id': 12, 'name': 'Foo'}
I face the same problem at current moment and trying to find solution.
#Jean-François Fabre♦ 's answer can't solve my own problem - it freezes arguments values at the moment of initializing Child class and if it change in future your Child class never knows about it.
I made some modifications to fix it:
class Parent(object):
def __init__(self, id, name):
self.id = id
self.name = name
self.__parent_vars = ['id', 'name'] # make a copy
def print_values(self):
res = {}
for el in self.__parent_vars:
res[el] = vars(self)[el]
return res
class Child(Parent):
def __init__(self, id, name, last_name, age):
Parent.__init__(self, id, name)
self.last_name = last_name
self.age = age
Lets test it:
c = Child(12,"Foo","whatever",34)
res1 = c.print_values()
print(res1)
c.id = 24
res2 = c.print_values()
print(res2)
Output:
{'id': 12, 'name': 'Foo'}
{'id': 24, 'name': 'Foo'}
Now it works as I expected BUT I need to create additioanl variable for it. So if I want to pickle, for example, my class it will also pickle this additional variable that I don't need. Is it possible to do the same actions qwithout creating additional variable?
Related
I know that each instance will inherit that attribute, but I want a function or should I call it a method of that class to return the set of all instances created of that class.
So let's say I created 3 instances and call a method from the last one that will return all the previously created instances as well as the one that I am calling it from.
I was able to achieve it by making a list, but would it be possible to return a set?
Is there some kind of constructor that I am missing for it?
class Bee():
instances = []
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
self.instances.append(f"{self.identifier} {self.name}")
return f"{self.identifier} {self.name}"
def get_hive(self):
return self.instances
Normally you would create Hive as a separate class and put the Bees inside. You then have a clear and explicit data structure whose job includes keeping track of all Bees created.
Something like:
class Hive:
def __init__(self):
self.bees = []
def add_bee(self, bee):
self.bees.append(bee)
class Bee:
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
return f"Bee({self.name}, {self.identifier})"
def __repr__(self):
return str(self)
# User code example
hive = Hive()
b1 = Bee('My Bee', 0)
b2 = Bee('Some Other Bee', 1)
hive.add_bee(b1)
hive.add_bee(b2)
print(hive.bees) # display all bees inside the hive
person.py
class Person:
"""---A class representing a person---"""
# Person constructor
def __init__(self,n,a):
self.full_name = n
self.age = a
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
Person.__init__(self,n,a)
self.school = s
driver.py
from person import *
a = Student("Alice", 19, "Univ")
It throws TypeError: __init__() takes 3 positional arguments but 4 were given
I tried to change Student class to the following:
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
super().__init__(n,a)
self.school = s
The error still exists.
Why does this happen? Is super() keyword required to add new attributes?
EDIT: The problem is solved. There was an indentation issue in the source code rendering this strange behavior, hence the question should be closed.
This line:
Person.__init__(self,n,a)
Is the problem. Recall that methods are automatically passed a reference to themselves, so you just passed a second one.
There's also a well-established pattern for this:
class Person
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, school, *args):
super().__init__(*args)
self.school = school
student = Student('Washington Elementary', "Johnny Go'gettem", 10)
although note that simply removing your reference to self in the Person.__init__ call inside Student.__init__ would be sufficient.
Note that you can override the default method behavior with a couple of decorators that become quite useful in certain situations. Neither apply here, but just a bit of knowledge to tease your brain a bit:
def SomeClass:
attr = "class-scoped"
def __init__(self):
self.attr = "instance-scoped"
def some_method(self):
return self.attr == "instance-scoped"
#classmethod
def some_classmethod(cls):
return cls.attr == "class-scoped"
#staticmethod
def some_staticmethod():
return "I'm not given a \"self\" parameter at all!"
classmethods are particularly useful as alternate constructors
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
#classmethod
def from_tuple(cls, tup) -> "Person":
"""Expects a tuple of (name, age) and constructs a Person"""
name, age = tup
return cls(name, age)
#classmethod
def from_dict(cls, dct) -> "Person":
"""Expects a dictionary with keys "name" and "age" and constructs a Person"""
try:
name = dct['name']
age = dct['age']
except KeyError:
raise ValueError(f"Dictionary {dct} does not have required keys 'name' and 'age'")
else:
return cls(name, age)
I want to create a class that has two characteristics:
Inherits a base class (all attributes and methods) automatically
Takes an object of that base class as an argument.
I want these two characteristics because I want to automatically inherit all the attributes and methods of the previous object (base class object) without having to do something like use the __init__ method since this will cause recalculation of the already computed initialization. And since there will be a lot of methods and attributes I don't think its good practice to do it manually.
My idea of the code would look something like this.
class BaseClass(object):
def __init__(self, name, date):
self.name = name
self.date = date
def get_name_date(self):
self.name_date = self.name +self.date
class UpperClass(BaseClass):
def __init__(self):
self.date_name = self.date + self.name
I know the code above will not work and I dont want to do something like:
class UpperClass(BaseClass):
def __init__(self):
super(BaseClass, self).__init__(name, date)
self.date_name = self.date + self.name
Cause this will re-do calculations I already have.
Maybe inheritance is not what I'm looking for, any pointers?
Is this what you are looking for?
class BaseClass(object):
def __init__(self, name, date):
self.name = name
self.date = date
def get_name_date(self):
self.name_date = self.name +self.date
class UpperClass:
def __init__(self, baseobject):
self.baseobject = baseobject
self.date_name = baseobject.date + baseobject.name
def __getattr__(self, item):
return getattr(self.baseobject, item)
o1 = BaseClass('thmei', 'may')
o2 = UpperClass(o1)
print(o1.date) # may
print(o2.date) # may
print(o2.date_name) # maythmei
Hi I am trying to use composition to create a new class using instances of another class when I try to turn the new object into a dictionary using __dict__, it's shows me <__main__.myobjec object at 0x00000000029CA908>, not sure am I using the __dict__ incorrectly though I have heard its related to new classes, any help greatly appreciated.
class User:
def __init__(self, name, job=None):
self.name = name
self.job = job
class Customer(User):
_ID = 100
def __init__(self, name, job=None):
self.name = name
self.job = job
class Account:
_ID = 0
def __init__(self, name, job=None):
self.customer = Customer(name , "Customer")
def __getattr__(self, attr):
return getattr(self.customer, attr)
>>> A = Account("Abdi")
>>> A.__dict__
{'customer': <__main__.Customer object at 0x109fdbfc8>}
>>>
You need to implement the __repr__ method to represent all of the instances of the Customer class.
def __repr__(self): return repr(self.__dict__) # the dictionary of attributes in __repr__
I am trying to figure out a really simple problem but still I can't quite get how a class works. For example, in case I wanted to create a class called "Friend" with an attribute called "name", does this mean I will have to give a variable called "name"before anything else ? Then how can i define the constructor to allow the specification of "name"? Is this code nonsense? Thanks in advance for any response
class Friend:
def __init__(self,name):
self.set_name(name)
def set_name(self,name):
self.name=name
def get_name(self):
return self.name
That code is not nonsense as in it accomplishes what you want to accomplish. It is not very pythonic, though. There are no reason you should use getter or setters. Just access the attributes directly. Like
class Friend:
def __init__(self,name):
self.name = name
you can instantiate your class by
friend = Friend('jeremy')
now just access name directly
print friend.name # jeremy
There is a good amount to learn about python classes luckily python provides excellent documentation for whatever version you are on.
in this example, to create a new friend you need to instantiate it with a name.
What you are referring to is default keyword arguments. The way you have specified it in your example means that name is required in the constructor. The way to make it default (and be able to be set after the constructor) would look like this:
class Friend(object):
def __init__(self,name=''):
self.name = name
def set_name(self,name):
self.name=name
def get_name(self):
return self.name
Now your class can be instantiated without a name:
aFriend = Friend()
As suggested in comments, it is not "considered pythonic" to have setters and getters for a basic attribute. But, if that attribute requires computation, then you can make it a property:
class Friend(object):
def __init__(self, firstname='', lastname=''):
self.firstname = firstname
self.lastname = lastname
#property
def firstname(self):
return self._first
#firstname.setter
def firstname(self, n):
self._first = n.capitalize()
#property
def lastname(self):
return self._last
#lastname.setter
def lastname(self, n):
self._last = n.capitalize()
#property
def fullname(self):
return "{0} {1}".format(self.firstname, self.lastname)
f = Friend('frank')
f.lastname = 'smith'
f.firstname
# 'Frank'
f.lastname
#'Smith'
f.fullname
#'Frank Smith'