How to give names to class instances instantiated with "for" loops? - python

As an attribute to a certain class, I'm instantiating a bunch of objects of another class. My problem is that they have ugly names for memory addresses. How do I give them proper names?
class CaseReader(object):
def __init__(self, path):
cases_paths = glob(path + '//*')
cases_names = os.listdir(path)
self.case = [Case(i) for i in cases_paths]
Upon running:
a = CaseReader(path)
a
Out[4]: <__main__.CaseReader at 0x1c6dfc7fa88>
a.case
Out[5]:
[<__main__.Case at 0x1c6dfc99fc8>,
<__main__.Case at 0x1c6dfc99dc8>,
<__main__.Case at 0x1c6dfcaf3c8>,
<__main__.Case at 0x1c6dfcaf448>,
<__main__.Case at 0x1c6dfcaf208>]

Overwrite the __str__ function in the class definition and print what ever attributes you want to see, when you print the reference of the object.
Sample Code
class A:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name

Related

How to print one Attribute in a class in Python?

simply i have a class that has a method to print a specific attibute in a class example:
class Attr:
def __init__(self, name,
wattage):
self.name = name
self.wattage = wattage
def print_attr(self):
print("attribute in class is " + getattr(Attr, wattage)
the expected output is:
attribute name is wattage: test
You don't need a helper function for this, Python does not have restricted access. Simply access the attribute directly:
a = Attr("Name", 10)
print(a.wattage)
If you truly want to make a print method like this, there are two ways to do it:
class Attr:
def __init__(self, name, wattage):
self.name = name
self.wattage = wattage
def print_wattage(self):
print(f'Wattage in *instance* is {getattr(self, "wattage")}') # getattr gets by string name
def print_name(self):
print(f"Name in *instance* is {self.name}")

How to access attribute of object from another object's method, which is one of attributes in Python?

I would like to know if it's possible, and if yes, how to access attribute(s) of a "super" class instance, when having composition implemented.
Example provided below is only to provide idea here and setup common ground on further explanations.
I want to have access to "id" attribute for an instance of MiniVan directly from object "door" (type DoorElement).
My Code
class Car:
def __init__(self, _id):
self.id = _id
class CarElement:
def __init__(self, name):
self.name = name
def get_car_id(self):
# Body which will access value of attribute "id"
return car_id
class MiniVan(Car):
def __init__(self, _id):
super(MiniVan, self).__init__(_id)
self.door = DoorElement('door')
self.engine = EngineElement('engine')
class DoorElement(CarElement):
def __init__(self, name):
super(DoorElement, self).__init__(name)
class EngineElement(CarElement):
def __init__(self, name):
super(EngineElement, self).__init__(name)
def main():
mini_van = MiniVan(123)
id_from_door = mini_van.door.get_car_id()
id_from_engine = mini_van.engine.get_car_id()
print(id_from_door) # Expected output 123
print(id_from_engine) # Expected output 123
if __name__ == '__main__':
main()
Expected:
Printed out twice "123"
What I've tried:
Passing required attribute during creating object
I know that I could just define init method with passing "car_id" but for some reasons I would love to avoid it if possible. If not, I would propably just go for it.
to set class attribute, and then call it from CarElement class within classmethod e.g.:
#classmethod
def get_id(cls):
return Car.id
But issue with this solution is that, I can have many child-classes for Car class (MiniVan, Truck, etc.) and I want have it still working.
Trying to use descriptor
def __get__(self, instance, owner):
return instance.id
But I could understand it wrong, and actually getter (as far as I understand clean code) should return instance of a class and not any attribute.
Additional Info
I will ALWAYS use CarElement (or child classes) instances as attributes of instance of Car (or child classes) instances - different usage will be treated as use-error
There can be a lot of different child classes of Car class, but always within inheritance way ( Car <- RacingCar(Car) <- FormulaOneCar(RacingCar) ) but no composition
In order for your code to work, you would have to initialize all CarElement-s with car_id. Currently, the error you are getting comes from lack of such a variable in the scope of the method. My idea of a change is this:
class CarElement:
def __init__(self, name, car_id):
self.name = name
self.car_id = car_id
def get_car_id(self):
# Body which will access value of attribute id
return self.car_id
I can't see any other magic way.

Generate classes based on a list of names in Python

The approach might be just wrong to begin with, but I'm trying to do the following:
class Material:
pass
class Vacuum(Material):
def __str__(self):
return 'vacuum'
class Aluminum(Material):
def __str__(self):
return 'aluminum'
class Graphite(Material):
def __str__(self):
return 'graphite'
class Beryllium(Material):
def __str__(self):
return 'beryllium'
I have different pieces of code that deals with different materials. Instead of passing a string as argument to that other pieces I would prefer to give it objects. This allows to have tab-completion with ipython and it is also a way to enforce the type.
To avoid changing the already written pieces, those will just do str(argument): if it is a string it recovers the old behavior, if it is one of the objects it will work.
The question is now: I want to support a given list of materials:
allowed_materials = ['vacuum', 'aluminum', 'graphite',]
and that list might be growing. Instead of manually writing the classes, how could I generate them based on the list?
You can define a metaclass that can generate your classes for you.
class mattype(type):
def __new__(mcls, name, bases=(), d=None):
def __str__(self):
return name.lower()
if not d:
d = {}
d['__str__'] = __str__
bases = (*bases, Material)
return super().__new__(mcls, name.title(), bases, d)
allowed_materials = ['vacuum', 'aluminum', 'graphite',]
classes = {name: mattype(name) for name in allowed_materials}
str(classes['vacuum']())
# 'vacuum'
If you do not need different class name for different material you can simply initialise it inside the material class. If not I will delete my answer.
class Material:
def __init__(self,name):
self.name=name
def __str__(self):
return self.name
allowed_materials = ['vacuum', 'aluminum', 'graphite',]
obj_lst=[Material(material) for material in allowed_materials]
for obj in obj_lst:
print(str(obj))
output:
vacuum
aluminum
graphite
I ended up doing the following, also adding objects to the module.
import sys
class Material:
def __str__(self):
return self.__class__.__name__
pass
print(sys.modules[__name__])
_materials = ['Copper', 'Vacuum']
for m in _materials:
setattr(sys.modules[__name__], m, type(m, (Material,), {})())

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.

Can I iterate over a class in Python?

I have a class that keeps track of its instances in a class variable, something like this:
class Foo:
by_id = {}
def __init__(self, id):
self.id = id
self.by_id[id] = self
What I'd like to be able to do is iterate over the existing instances of the class. I can do this with:
for foo in Foo.by_id.values():
foo.do_something()
but it would look neater like this:
for foo in Foo:
foo.do_something()
is this possible? I tried defining a classmethod __iter__, but that didn't work.
If you want to iterate over the class, you have to define a metaclass which supports iteration.
x.py:
class it(type):
def __iter__(self):
# Wanna iterate over a class? Then ask that class for iterator.
return self.classiter()
class Foo:
__metaclass__ = it # We need that meta class...
by_id = {} # Store the stuff here...
def __init__(self, id): # new isntance of class
self.id = id # do we need that?
self.by_id[id] = self # register istance
#classmethod
def classiter(cls): # iterate over class by giving all instances which have been instantiated
return iter(cls.by_id.values())
if __name__ == '__main__':
a = Foo(123)
print list(Foo)
del a
print list(Foo)
As you can see in the end, deleting an instance will not have any effect on the object itself, because it stays in the by_id dict. You can cope with that using weakrefs when you
import weakref
and then do
by_id = weakref.WeakValueDictionary()
. This way the values will only kept as long as there is a "strong" reference keeping it, such as a in this case. After del a, there are only weak references pointing to the object, so they can be gc'ed.
Due to the warning concerning WeakValueDictionary()s, I suggest to use the following:
[...]
self.by_id[id] = weakref.ref(self)
[...]
#classmethod
def classiter(cls):
# return all class instances which are still alive according to their weakref pointing to them
return (i for i in (i() for i in cls.by_id.values()) if i is not None)
Looks a bit complicated, but makes sure that you get the objects and not a weakref object.
Magic methods are always looked up on the class, so adding __iter__ to the class won't make it iterable. However the class is an instance of its metaclass, so the metaclass is the correct place to define the __iter__ method.
class FooMeta(type):
def __iter__(self):
return self.by_id.iteritems()
class Foo:
__metaclass__ = FooMeta
...
Try this:
You can create a list with a global scope, define a list in the main module as follows:
fooList = []
Then add:
class Foo:
def __init__(self):
fooList.append(self)
to the init of the foo class
Then everytime you create an instance of the Foo class it will be added to the fooList list.
Now all you have to do is iterate through the array of objects like this
for f in fooList:
f.doSomething()
You can create a comprehension list and then call member methods as follows:
class PeopleManager:
def __init__(self):
self.People = []
def Add(self, person):
self.People.append(person)
class Person:
def __init__(self,name,age):
self.Name = name
self.Age = age
m = PeopleManager()
[[t.Name,t.Age] for t in m.People]
call to fill the object list:
m = PeopleManager()
m.Add( Person("Andy",38))
m.Add( Person("Brian",76))
You can create a class list and then call append in the init method as follows:
class Planet:
planets_list = []
def __init__(self, name):
self.name = name
self.planets_list.append(self)
Usage:
p1 = Planet("earth")
p2 = Planet("uranus")
for i in Planet.planets_list:
print(i.name)

Categories

Resources