Trying to put classes into for loops in Python 3.5 - python

I am trying to make a for loop, which iterates through a range, and makes classes. How could I do this? How could the classes be named?
Here is what I have and mean.
for i in range(6):
class Name(str(i)): #I want the class name to be Name and then the number as a string. So the classes will be called 'Name1', Name2' etc
pass
I'm making an RPG type game and I want a range of different monster types. I want to be able to generate classes for each monster type. I want each monster to be a level higher than the last so the health and other stats will be multiplied by the previous monster's stats

To answer the question specifically, you would use the 3 argument form of type to create a metaclass:
>>> classes=[type('Name'+str(i), (), {}) for i in range(6)]
>>> classes
[<class '__main__.Name0'>, <class '__main__.Name1'>, <class '__main__.Name2'>, <class '__main__.Name3'>, <class '__main__.Name4'>, <class '__main__.Name5'>]
>>> classes[0].__name__
'Name0'
The form Bar=type('Bar', (), {}) is analogous to:
class Foo:
pass
Instantiating an instance would be:
>>> Bar=type('Bar', (), {})
>>> Bar()
<__main__.Bar object at 0x102c90fd0>
vs
>>> class Foo:
... pass
...
>>> Foo()
<__main__.Foo instance at 0x102cde5f0>

If you want to make brand new classes with names as if defined manually, you're usually stuck with format-ing and eval-ing strings. This is how Python implements collections.namedtuple; it's implemented in Python code using a template string that it fills in programmatically then evals.
Take a look at the namedtuple implementation for an example of how you might do something like this.
You can also make classes programmatically using the three argument type constructor, which lets you explicitly provide a name, bases and class dictionary, so making Name# six times with no special base classes or class attributes or member functions could be made and assigned to globals via:
globals().update({name: type(name, (), {}) for name in map('Name{}'.format, range(6))})

One you create a class, it's available like any other symbol. So try this:
def make_class(name:str):
class c:
_classname = name
pass
return c
Now you have an object of type class (not really, but pretend), you can install it wherever you like:
import sys
my_namespace = sys.modules[__name__].__dict__
for i in range(1,3):
name = "my_class_name_{}".format(i)
cls = make_class(name)
my_namespace[name] = cls
obj1 = my_class_name_1()
print(type(obj1), obj1._classname)
obj2 = my_class_name_2()
print(type(obj2), obj2._classname)

The more pythonic way would be a list of monster objects accessed like monsters[1]. You can do it like this.
monsters = []
for i in range(6):
monsters.append(monster())
To do it your way you can see the other answers in the thread.

You can generate classes in a loop, but it is the sign of a bad design, and always can be avoided.
This code generates classes and stores them in an array
class Base():
pass
classes = [Base]
for i in range(6):
class Name(classes[-1]):
pass
classes.append(Name)
print(classes[0] != classes[-1]) # True
print(isinstance(classes[-1](), Base)) # True
It would be much better if all the monsters would be of the same type, and there would be some kind of "metaclass" for monster types. This means that you would have a dynamic structure which is language independent, can be changed even in runtime, and monster types can be loaded from resource files:
class Monster:
def __init__(self, mtype):
self.mtype = mtype
self.health = mtype.max_health
# ...
class MonsterType:
def __init__(self, mtype = None):
self.parent_type = mtype
self.max_health = 10 if mtype is None else mtype.max_health + 10
# ...
mtypes = [MonsterType()]
for i in range(10):
mtypes.append(MonsterType(mtypes[-1]))
monster = Monster(mtypes[3])
# monster.health == 40

First things first: as other answers advised, be sure to think through this design decision. Creating classes in a loop is a possibly a red flag that your design is flawed. Moving on.
You can do this using metaclasses, or the type function. In fact, type is the default metaclass. It is the class of all built-in classes:
>>> print(type(int))
<class 'type'>
...it is the class of the basic object class:
>>> print(type(object))
<class 'type'>
...type is even its own class:
>>> print(type(type))
<class 'type'>
...and unless specified otherwise, all classes you create are themselves type objects:
>>> class MyClass:
pass
>>> print(type(MyClass))
<class 'type'>
All metaclasses - including type - can be used to create classes. When used this way, type takes 3 arguments:
class name (a string)
a tuple containing the parent classes
a dictionary containing class attributes/members
Probably the simplest way to accomplish your goal is to first create a dictionary to hold your classes:
Name = {(i + 1): None for i in range(6)}
We will populate the dictionary values using the type metaclass:
for num in Name:
Name[num] = type(('Name' + str(i + 1)), (object,), {})
We can accomplish all of the above with this one-liner:
Name = {(i + 1): type(('Name' + str(i + 1)), (object,), {}) for i in range(6)}
In the example above we are inheriting from object and providing no class members, but this can be adjusted as needed.
If you need more customization in your dynamically created classes, a good option is to use a base class with the starting functionality you require:
class BaseMonster:
def method1(self):
# do stuff
Name = {(i + 1): type(('Name' + str(i + 1)), (BaseMonster,), {}) for i in range(6)}
n1 = Name[1]()
n1.method1()
Recall: type is the default metaclass. However, even more custimization can be accomplished by creating your own custom metaclass. You do this by inheriting a new class from type:
class MetaMonster(type):
def __new__(mclass, number, bases, dct):
name = 'Name' + str(number + 1)
return super().__new__(mclass, name, (BaseMonter,) + bases, dct)
And use it like this:
Name = {(i + 1): MetaMonster(i, tuple(), {}) for i in range(6)}
n1 = Name[1]()
n1.method1()
Note that you no longer have to provide the BaseMonster argument, nor do you have to construct the string representing the class name; this is all taken care of in the MetaMonster.__new__ method.

Related

Changing an object's class while maintaining its attributes and functions

If I have 2 classes defined like this:
class A(object):
a = 10
class B(A):
b = 20
If I create an object:
c = A()
And then do:
c.__class__ = B
Is it a valid way to change ('upgrading') the class of the object, maintaining the primary class attributes and methods and gaining the secondary class attributes and methods?
If true, this only makes sense for this cases where the class to which we are changing the object inherits from the previous class? Best regards.
UPDATED:
To give more context.
I have the following class EmbrionDevice.
class EmbrionDevice(object):
def __init__(self, device_info, *args, **kwargs):
super(EmbrionDevice, self).__init__(*args, **kwargs)
# Serial number unique 64-bit address factory-set
self.shl = device_info['source_addr_long']
# 16-bit network address
self.my = device_info['source_addr']
# Node identifier
self.ni = device_info['node_identifier']
# Parent Address
self.pa = device_info['parent_address']
# Device type, 0-coordinator, 1-router, 2-End Device
self.dt = device_info['device_type']
# Device type identifier xbee or Digi device
self.dd = device_info['device_type_identifier']
# Device attributes summary in a dictionary
self.info = device_info
# Embrion future function
self.function_identifier = None
# Device state definition
self.state = DEV_STATE_CODES['embrion']
self.status = DEV_STATUS_CODES['no status']
That i would later like to change/upgrade, to one of the following specific device classes:
class PassiveDevice(EmbrionDevice):
pass
class ActiveDevice(EmbrionDevice):
pass
Basically i wanted to ease my copy, avoiding the copy of all the attributes.
This is not a valid way to change class of a instance object, A simple example can demonstrate it :-
class A(object):
a = 10
def __init__(self):
self.b = 20
self.c = 30
class B(A):
d = 35
def __init__(self):
self.x = 70
self.y = 80
c = A()
c.__class__ = B
print c
<__main__.B object at 0x02643F10>
So now c is instance of class B, Try printing instance attributes:
print c.x
print c.y
It says:
AttributeError: 'B' object has no attribute 'x'
That's definitely a hack, and this is also a hack, but I find it do be a bit cleaner:
In [1]: class A(object):
...: a = 10
...:
In [2]: class B(A):
...: b = 20
...:
In [3]: c = A()
In [4]: new_c = B()
In [5]: new_c.__dict__.update(c.__dict__.copy())
In [7]: repr(new_c)
Out[7]: '<__main__.B object at 0x102f32050>'
In [8]: new_c.b
Out[8]: 20
I'm not sure if your approach would work or not, but, this way, you're copying the properties of the old object into a new object that was properly instantiated. If you change .__class__, you can't guarantee that the old variable will reference a properly-created new-class object, as __init__(), __new__(), etc. wouldn't run.
To copy functions, and, this is ugly... but, you could take an approach like this:
In [18]: for name, obj in c.__class__.__dict__.iteritems():
....: if hasattr(obj, '__call__'):
....: # Copy the function.
....:
test
There are various hacky methods of adding functions to an existing object dynamically. They're all ugly, but, they can be found here.
You have a misunderstanding of what are "class attributes" in Python -
All instance attributes are kept in the instance itself: it does have a __dict__ attribute which is a dictionary where all the attributes defined by code like self.shl = device_info['source_addr_long'] is kept. (This statement creates an shl entry on that dict, for example).
These assignments are run inside the __init__method. If you change an object's class by assigning to its __class__ , it works in a sense: that is its new class. The methods the new class may have defined are now acessible. But all the attributes which were set in the previous class' __init__ still exist, because they are set on the instance's __dict__ that was not changed;. From what I got, this may be exactly what you want - but please note that methods on the original class (as well as class attributes - i.e., attributes defined on the class body itself) will not be acessible, unless the new class itself inherits from the original class. As in the example you show, this is what you are doing, this approach might actually work for you.
But be careful, and do some extensive unit testing, and testing on the interactive console.
An alternative for you might be to use zope.interface - tis will allow you to have a single object, but that "looks like" an object with different attributes and methods to other parts of the code, which might need an specific interface.

Does there exist empty class in python?

Does there exist special class in python to create empty objects? I tried object(), but it didn't allow me to add fields.
I want to use it like this:
obj = EmptyObject()
obj.foo = 'far'
obj.bar = 'boo'
Should I each time(in several independent scripts) define new class like this?
class EmptyObject:
pass
I use python2.7
types.SimpleNamespace was introduced with Python 3.3 to serve this exact purpose. The documentation also shows a simple way to implement it yourself in Python, so you can add it to your pre-Python 3.3 setup and use it as if it was there (note that the actual implementation is done in C):
class SimpleNamespace (object):
def __init__ (self, **kwargs):
self.__dict__.update(kwargs)
def __repr__ (self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__ (self, other):
return self.__dict__ == other.__dict__
But of course, if you don’t need its few features, a simple class Empty: pass does just the same.
If you are looking for a place holder object to which you can add arbitrary static members, then the closest I got is an empty lambda function.
obj = lambda: None # Dummy function
obj.foo = 'far'
obj.bar = 'boo'
print obj.foo, obj.bar
# far boo
Remember: obj is not an object of a class, object doesn't mean class instance, because in Python classes and functions are objects at runtime just like class instances
There is no types.SimpleNamespace in Python 2.7, you could use collections.namedtuple() for immutable objects instead:
>>> from collections import namedtuple
>>> FooBar = namedtuple('FooBar', 'foo bar')
>>> FooBar('bar', 'foo')
FooBar(foo='bar', bar='foo')
Or argparse.Namespace:
>>> from argparse import Namespace
>>> o = Namespace(foo='bar')
>>> o.bar = 'foo'
>>> o
Namespace(bar='foo', foo='bar')
See also, How can I create an object and add attributes to it?
You can create a new type dynamically with the fields you want it to have using the type function, like this:
x = type('empty', (object,), {'foo': 'bar'})
x.bar = 3
print(x.foo)
This is not entirely what you want though, since it will have a custom type, not an empty type.

Python with MongoEngine - access to the list of custom model attributes

Looking for easy access to the list of custom model attributes in some Python model classes I have. I'm using MongoEngine as my ORM, but the question is a general inheritance and OOP.
Specifically, I'd like to be able to access custom model attributes from a method in a Mixin class that I'll inherit from in all of my model classes.
Consider the following class structure:
class ModelMixin(object):
def get_copy(self):
"""
I'd like this to return a model object with only the custom fields
copied. For the City object below, it would run code equivalent to:
city_copy = City()
city_copy.name = self.name
city_copy.state = self.state
city_copy.popluation = self.population
return city_copy
"""
class City(BaseModel, ModelMixin):
name = orm_library.StringField()
state = orm_library.StringField()
population = orm_library.IntField()
This would allow the following:
>>> new_york = City(name="New York", state="NY", population="13000000")
>>> new_york_copy = new_york.get_copy()
But, it has to work for arbitrary models. Somehow, it has to determine what custom attributes have been defined in the subclass, instantiate an instance of that subclass, and copy over only those custom properties, without copying the builtin attributes and methods from the parent BaseModel class (which has a ton of random stuf in it I'm not concerned with.
Anyone know how I could do this?
I think you have several tools at your disposal to pull this off
(and if the code I have below doesn't quite do what you want, you should
be able to adapt it pretty readily). Namely:
the __class__ attribute will give you the class of an object
the vars() function will enumerate the attributes of an object (or class)
the setattr() and getattr() functions can let you manipulate
an arbitrary attribute by name.
to distinguish which class attributes are interesting to you
(in your case, those that are MonogEngine fields) I check the
class type of the model attribute itself; quick glance of MongoEngine
sources seems to indicate that you'll want to change that to 'BaseField'
class ModelMixin(object):
def get_copy(self):
# Get the class for the
C = self.__class__
# make a new copy
result = C()
# iterate over all the class attributes of C
# that are instances of BaseField
for attr in [k for k,v in vars(C).items() if v.__class__ == BaseField]:
setattr(result, attr, getattr(self, attr))
return result
To test the above (creating dummy classes for MongoEngine models/fields)
class BaseField(object):
pass
class BaseModel(object):
baseField = BaseField()
class City(BaseModel, ModelMixin):
x = BaseField()
y = BaseField()
c = City()
c.x = 3
c.y = 4
c.baseField = 5
d = c.get_copy()
print d.x # prints '3'
print d.y # prints '4'
print d.baseField # correctly prints main.BaseField, because it's not set for d

Polluting a class's environment

I have an object that holds lots of ids that are accessed statically. I want to split that up into another object which holds only those ids without the need of making modifications to the already existen code base. Take for example:
class _CarType(object):
DIESEL_CAR_ENGINE = 0
GAS_CAR_ENGINE = 1 # lots of these ids
class Car(object):
types = _CarType
I want to be able to access _CarType.DIESEL_CAR_ENGINE either by calling Car.types.DIESEL_CAR_ENGINE, either by Car.DIESEL_CAR_ENGINE for backwards compatibility with the existent code. It's clear that I cannot use __getattr__ so I am trying to find a way of making this work (maybe metaclasses ? )
Although this is not exactly what subclassing is made for, it accomplishes what you describe:
class _CarType(object):
DIESEL_CAR_ENGINE = 0
GAS_CAR_ENGINE = 1 # lots of these ids
class Car(_CarType):
types = _CarType
Something like:
class Car(object):
for attr, value in _CarType.__dict__.items():
it not attr.startswith('_'):
locals()[attr] = value
del attr, value
Or you can do it out of the class declaration:
class Car(object):
# snip
for attr, value in _CarType.__dict__.items():
it not attr.startswith('_'):
setattr(Car, attr, value)
del attr, value
This is how you could do this with a metaclass:
class _CarType(type):
DIESEL_CAR_ENGINE = 0
GAS_CAR_ENGINE = 1 # lots of these ids
def __init__(self,name,bases,dct):
for key in dir(_CarType):
if key.isupper():
setattr(self,key,getattr(_CarType,key))
class Car(object):
__metaclass__=_CarType
print(Car.DIESEL_CAR_ENGINE)
print(Car.GAS_CAR_ENGINE)
Your options fall into two substantial categories: you either copy the attributes from _CarType into Car, or set Car's metaclass to a custom one with a __getattr__ method that delegates to _CarType (so it isn't exactly true that you can't use __getattr__: you can, you just need to put in in Car's metaclass rather than in Car itself;-).
The second choice has implications that you might find peculiar (unless they are specifically desired): the attributes don't show up on dir(Car), and they can't be accessed on an instance of Car, only on Car itself. I.e.:
>>> class MetaGetattr(type):
... def __getattr__(cls, nm):
... return getattr(cls.types, nm)
...
>>> class Car:
... __metaclass__ = MetaGetattr
... types = _CarType
...
>>> Car.GAS_CAR_ENGINE
1
>>> Car().GAS_CAR_ENGINE
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Car' object has no attribute 'GAS_CAR_ENGINE'
You could fix the "not from an instance" issue by also adding a __getattr__ to Car:
>>> class Car:
... __metaclass__ = MetaGetattr
... types = _CarType
... def __getattr__(self, nm):
... return getattr(self.types, nm)
...
to make both kinds of lookup work, as is probably expected:
>>> Car.GAS_CAR_ENGINE
1
>>> Car().GAS_CAR_ENGINE
1
However, defining two, essentially-equal __getattr__s, doesn't seem elegant.
So I suspect that the simpler approach, "copy all attributes", is preferable. In Python 2.6 or better, this is an obvious candidate for a class decorator:
def typesfrom(typesclass):
def decorate(cls):
cls.types = typesclass
for n in dir(typesclass):
if n[0] == '_': continue
v = getattr(typesclass, n)
setattr(cls, n, v)
return cls
return decorate
#typesfrom(_CarType)
class Car(object):
pass
In general, it's worth defining a decorator if you're using it more than once; if you only need to perform this task for one class ever, then expanding the code inline instead (after the class statement) may be better.
If you're stuck with Python 2.5 (or even 2.4), you can still define typesfrom the same way, you just apply it in a slightly less elegant matter, i.e., the Car definition becomes:
class Car(object):
pass
Car = typesfrom(_CarType)(Car)
Do remember decorator syntax (introduced in 2.2 for functions, in 2.6 for classes) is just a handy way to wrap these important and frequently recurring semantics.
class _CarType(object):
DIESEL_CAR_ENGINE = 0
GAS_CAR_ENGINE = 1 # lots of these ids
class Car:
types = _CarType
def __getattr__(self, name):
return getattr(self.types, name)
If an attribute of an object is not found, and it defines that magic method __getattr__, that gets called to try to find it.
Only works on a Car instance, not on the class.

Using Variables for Class Names in Python?

I want to know how to use variables for objects and function names in Python. In PHP, you can do this:
$className = "MyClass";
$newObject = new $className();
How do you do this sort of thing in Python? Or, am I totally not appreciating some fundamental difference with Python, and if so, what is it?
Assuming that some_module has a class named "class_name":
import some_module
klass = getattr(some_module, "class_name")
some_object = klass()
I should note that you should be careful here: turning strings into code can be dangerous if the string came from the user, so you should keep security in mind in this situation. :)
One other method (assuming that we still are using "class_name"):
class_lookup = { 'class_name' : class_name }
some_object = class_lookup['class_name']() #call the object once we've pulled it out of the dict
The latter method is probably the most secure way of doing this, so it's probably what you should use if at all possible.
In Python,
className = MyClass
newObject = className()
The first line makes the variable className refer to the same thing as MyClass. Then the next line calls the MyClass constructor through the className variable.
As a concrete example:
>>> className = list
>>> newObject = className()
>>> newObject
[]
(In Python, list is the constructor for the list class.)
The difference is that in PHP, you represent the name of the class you want to refer to as a string, while in Python you can reference the same class directly. If you must use a string (for example if the name of the class is created dynamically), then you will need to use other techniques.
If you need to create a dynamic class in Python (i.e. one whose name is a variable) you can use type() which takes 3 params:
name, bases, attrs
>>> class_name = 'MyClass'
>>> klass = type(class_name, (object,), {'msg': 'foobarbaz'})
<class '__main__.MyClass'>
>>> inst = klass()
>>> inst.msg
foobarbaz
Note however, that this does not 'instantiate' the object (i.e. does not call constructors etc. It creates a new(!) class with the same name.
If you have this:
class MyClass:
def __init__(self):
print "MyClass"
Then you usually do this:
>>> x = MyClass()
MyClass
But you could also do this, which is what I think you're asking:
>>> a = "MyClass"
>>> y = eval(a)()
MyClass
But, be very careful about where you get the string that you use "eval()" on -- if it's come from the user, you're essentially creating an enormous security hole.
Update: Using type() as shown in coleifer's answer is far superior to this solution.
I use:
newObject = globals()[className]()
I prefer using dictionary to store the class to string mapping.
>>> class AB:
... def __init__(self, tt):
... print(tt, "from class AB")
...
>>> class BC:
... def __init__(self, tt):
... print(tt, "from class BC")
...
>>> x = { "ab": AB, "bc": BC}
>>> x
{'ab': <class '__main__.AB'>, 'bc': <class '__main__.BC'>}
>>>
>>> x['ab']('hello')
hello from class AB
<__main__.AB object at 0x10dd14b20>
>>> x['bc']('hello')
hello from class BC
<__main__.BC object at 0x10eb33dc0>

Categories