How can I spot class redefinitions? - python

Here is a very simple setup:
class A(object):
x = 1
class B(object):
x = 2
class C(object):
x = 3
class B(B):
x = 4
class C(object):
x = 5
print A.x, B.x, C.x
which outputs the expected:
1, 4, 5
is it possible to detect that B has been 'redefined' but C has been 'replaced'? either during the creation phase or subsequently by inspecting the objects?
These classes are being used as configuration objects, there is a loader class above this which imports the module and then introspects to find the classses and their attributes which are then used by the application to instance named parameters. e.g.
class tcp_port(number):
minimum = 1024
maximum = 2048
really python is just being used as a convenient scripting language to define parameters in the main app. These files are available for edit by that most dangerous of people: The Customer So the requirement is to be able to detect at run time if a class has (accidentally) reused a name that has already been defined, but to safely pass the occasional case where a class is redefined, but then tweaks or add some attribute:
class tcp_port(tcp_port):
maximum = 4096
So... I'm hoping for some runtime sanity checker rather than a lint-like solution. Also, this needs to happen when the class is defined, not instanced.
... is there global function which gets called to create a class (or any other object)? a bit like have a __new__ but in the global context?

If the class was redefined in-place, it will have old versions of itself hanging around in the MRO:
>>> [x for x in A.mro() if x.__name__ == A.__name__]
[__main__.A]
>>> [x for x in B.mro() if x.__name__ == B.__name__]
[__main__.B, __main__.B]
>>> [x for x in C.mro() if x.__name__ == C.__name__]
[__main__.C]
So you could detect that occurrence:
def has_redefinition_inplace(classobj):
types = [x for x in classobj.mro() if x.__name__ == classobj.__name__]
return len(types) > 1
This would not be 100% reliable, because there could be legitimately be colliding class names coming into the MRO from different modules.

A good linter will warn on re-declarations without usage. And a good IDE will lint directly in the editor.
For example, PyCharm will highlight these class names with a warning colour:
Another good way to spot these is with coverage in your test suite. Functions with names that were overwritten (common copy-paste errors) can not have test coverage in the function body.

I solved this by defining a parent class for all the classes then using a metaclass to keep a track of the names of the classes created, check for 'derived from itself' and then report any names that were reused.
#!/usr/bin/python
class _Object_Meta(type):
_defined = set()
def __init__(cls, name, bases, dct):
for base in bases:
if name == base.__name__:
break
else:
if name in _Object_Meta._defined:
raise RuntimeError("%s redefined" % name)
_Object_Meta._defined.add(name)
class Object(object):
__metaclass__ = _Object_Meta
class A(Object):
x = 1
class B(Object):
x = 2
class C(Object):
x = 3
class B(B):
x = 4
class C(Object):
x = 5

Related

Why is super() not behaving like I expected when assigning to a class variable of the base class?

I am attempting to experiment with classes so I can better understand what they do. I wanted to build a counter which records the number of instances of a class (MyClass):
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
class MyClass(ObjectCounter):
def __init__(self):
super().myclass_obj_count += 1 # AttributeError: 'super' object has no attribute 'myclass_obj_count'
m1 = MyClass()
m2 = MyClass()
m3 = MyClass()
print(ObjectCounter.myclass_obj_count)
Since that didn't work, I looked online for someone trying to do the same thing. Here is some code I found online. This works as expected, and I feel like I have a basic understanding of how this works. This is a better solution to the task I was attempting, but I'm not satisfied because I want to know how super() works.
class geeks:
counter = 0
def __init__(self):
geeks.counter += 1
g1 = geeks()
g2 = geeks()
g3 = geeks()
print(geeks.counter) # this gives an expected result
Therefore, I tried this instead:
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
class MyClass(ObjectCounter):
def __init__(self):
super().add_myclass()
my_class_1 = MyClass()
my_class_2 = MyClass()
my_class_3 = MyClass()
print(ObjectCounter.myclass_obj_count) # expected output: 3
Instead of getting the expected output of 3, I got an output of 0. Why is this happening?
First, be aware of the += operator; it's implementation is quite subtle:
a += b
becomes
a = a.__iadd__(b)
This perhaps strange definition allows python to support it even for immutable types (like strings).
Note what happens when used for a class variable that is referred to by the alias self
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
# effectively becomes:
# self.myclass_obj_count = self.myclass_obj_count.__iadd__(1)
This will introduce an instance variable of the same name, shadowing the class variable.
You don't even need the subclass to test this:
>>> x = ObjectCounter()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.myclass_obj_count
3
>>> ObjectCounter.myclass_obj_count
0
Referring to the class variable directly instead of using self fixes this
def add_myclass(self):
ObjectCounter.myclass_obj_count += 1
I'm hesitant to give definite answers of what happens under the hood when class variables, super() and assignments are used, other than it just doesn't work. Perhaps because it would be quite ambiguous of whether or not we are defining class variables or new instance variables.
super() won't let you assign to either;
class ObjectCounter:
myclass_obj_count = 0
def __init__(self):
self.x = 'test'
class MyClass(ObjectCounter):
def __init__(self):
super().__init__()
print(super().myclass_obj_count) # reading works just fine
print(type(super())) # this isn't actually exactly the same as "ObjectCounter"
super().myclass_obj_count = 123 # no good
super().x = 'foo' # also no good.
All in all, for any assignment to class variables you can use the class name itself.

How to initialise class variables just once using arguments passed to init

I want to share data across different instances of a class, but the data must be provided externally the first time the class is created.
I have written the snippet below.
class Foo(object):
_config = False
eggs = None
def __init__(self, spam, eggs=None):
if Foo._config:
# Assume initialized and eggs exists
print(Foo.eggs)
else:
if eggs is None:
raise ValueError('eggs must be provided the first time')
else:
Foo.eggs = eggs
Foo._config = True
print("Scrambled {}?".format(Foo.eggs))
self.spam = spam
print("Class variable - eggs: {}".format(Foo.eggs))
print("Instance variable - spam: {}".format(self.spam))
which seems to work ...
>>>Foo._config
False
>>>a = Foo('chicken', 'eggs')
Scrambled eggs?
Class variable - eggs: eggs
Instance variable - spam: chicken
>>>Foo._config
True
and the second time doesn't raise an error and shares the class variable
>>>b = Foo('duck')
eggs
Class variable - eggs: eggs
Instance variable - spam: duck
My question is whether this is a good approach? I have seen this question which suggests that including things in __init__ that are only called once is a bad idea, and I should use a metaclass?
My justification is that eggs will actually contain a very large pandas dataframe that I don't to repeat with each instance.
I would advise against using the class namespace.
see:
class holder():
x = 5
def __init__(self):
self.x = 6
return;
alpha = holder()
beta=holder()
beta.x = 4
holder.x = 100
print(holder.x)
print(alpha.x)
print(beta.x)
> 100
> 6
> 4
The scope of the variable gets diluted very quickly. I would reserve the class namespace for constants.
If you attempt to set a reference in the class namespace then you will have to generate the panda dataframe before. It will likely be easier to genereate it somewhere in your code before creating objects and then pass it by reference to each class.
As mentioned by #juanpa.arrivillaga : self.df = df
One way to do it is to create a #classmethod which you would call at the beginning in order to instantiate your constant values shared by all objects.

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.

Class management in python

I am working with python at the moment and wonder about something. I'm not too good with object programming, as I've always coded with imperative languages (C mostly).
So I'm asking myself.
Say I have an instance of class_1 called c1, declared this way:
c1 = class_1(bla bla bla...)
Say c1 has a heck lot of variable declarations inside, like
self.x = ...
self.y = ...
self.z = ...
#etc etc
Now, I have an instance of type class_2, called c2. Say c2 is declared INSIDE c1's init() function, like that
c2 = class_2(bla bla bla...)
Now, I wonder how I could... acces the objects of c1 from within c2?
Some could say I could make class_2 inherit from class_1. That's not exactly what I want in fact. The reason is that, class_1 should include in a logical way objects that are of type class_2, but class_2 needs to access variables from class_1!
If I would make class_2 inherit from class_1, I would always have to... declare class_2 objects in the external (to the classes) code. I want to work, in the external code, with class_1 objects. How can I reasonably do that? Starting to be a nightmare...
Thanks!
EDITED:
The context in which I use it... I have, as an assignment, to write a small space video game. I have to calculate trajectories in space. I also have a map. Some parameters such as acceleration, rotation speed, etc... Are specific to the 'map' : i.e. we design a map with obstacles and all, but depending on the map, the some physics constant vary.
I also have a 'physcis' class to handle different calculations related to the trajectory, position upating, etc etc.
So I just want to be able to use the many many many instances of different objects which are contained in the 'map' class, so that I can use these variables inside the physics class!
Is it legit?
Thanks!
From what I can gather from your question, each class_2 is related to a particular class_1. I'll call that class_1 the parent object.
You want to be instantiating a class_2 from within the parent, so something like
class Class_1(object):
def __init__(self):
self.x = 1 # Your instance variables here
self.y = 2
self.class_2_instance = Class_2()
class Class_2(object):
def __init__(self):
pass
is what you want. But you need to access stuff from the parent inside class_2, right?
Simply pass the parent into class_2:
class Class_1(object):
def __init__(self):
self.x = 1 # Your instance variables here
self.y = 2
self.class_2_instance = Class_2(self)
class Class_2(object):
def __init__(self, class_1_instance):
self.class_1_instance = class_1_instance
And now you can, from within class_2, access class_1's variables.
print(self.class_1_instance.x) # prints 1
edit: You've clarified something about your question in the comments since I started writing this response, so now that I know what c1 and c2 are, I can show you how you would use the code I've written here:
c1 = Class_1()
c1 will now have an instance of Class_2 contained within it. That instance will be able to access all of the properties of c1.
One trick is to pass a parent parameter into the class2.__init__ and keep it e.g.:
class class2:
def __init__(self, parent, ):
""" Create an instance of class2 saving the parent """
# You might wish to assert that parent is type class1 here
self.parent = parent
This will let you access class1 members from within class2 as self.parent.member which seems to be what you need.
Even if you can define a class within a class I would urge you not to - it would lead to unclear code and I suspect would actually have more of an overhead than you might expect.
You can define class2 in the same file as class1 and class1 can either inherit from it or simply have members of the type class2 but if you call your class2 an underscored name such as _class2_ then by convention you are making them private to the scope in which they are defined, in this case the file where they are defined, (N.B. In python private is a convention - not enforced - more a gentlemen's agreement with the warning that private members may be modified/removed at any new version).
Note that defining a class is not the same as creating a class instance.
Just to see if it could be done I tried:
Python 2.7.5+ (default, Sep 19 2013, 13:48:49)
[GCC 4.8.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
... class B:
... def __init__(self):
... self.b = 3
... def __init__(self):
... self.b = B()
... self.a = 2
...
>>> a = A()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __init__
NameError: global name 'B' is not defined
This is because the rest of class A does not exist until init is finished, so class B needs to be defined inside the init before it is used -
>>> class A:
... def __init__(self):
... class B:
... def __init__(self):
... self.b = 3
... self.a = 2
... self.b = B()
...
>>> a=A()
>>> a.b.b
3
>>>

Is it possible to create a completely new instance of the members of a parent class?

Python version 2.7.
Scenario
class A(object):
x = 0
class B(A):
pass
ai = A()
bi = B()
#Then:
id(ai.x) == id(bi.x)
>>> True
What I'd like to know is if there is a way, other than having all the class member definitions in __init__ to have instances of class B have their own copies of x without having to redefine them in class B?
Perhaps some type() tricks?
Haven't found anything yet, but I'll keep looking, figured here would be the best place to find an answer.
Any insight is greatly appreciated.
edit: grammars
edit2 To clarify as I didn't really use the best example.
class Y(object):
def __init__(self):
self.z = 0
class A(object):
x = Y()
class B(A):
pass
ai = A()
bi = B()
id(ai.x) == id(bi.x)
>>> True
ai.x.z = 3
id(ai.x) == id(bi.x)
>>> True
Now the issue occurs that as I don't reassign x in class B they both point to the same instance of class Y even if members of the instance of class Y change.
If you're familiar with Django I'm almost recreating how their Forms work. I didn't decide to go with a metaclass for the forms and then build up a dictionary of the fields everytime a new instance is created, perhaps I will need to switch to using a metaclass.
edit the third I'm realising now the impossibility I'm asking of the interpreter. As once I assign an instance of a class to a member there's no way for the inherited classes to know how to create a new instance of that class. Therefore for my problem it seems metaclasses are the only solution.
For your example, it doesn't matter with immutable objects. bi references a new object if it is ever assigned.
class A(object):
x = 0
class B(A):
pass
ai = A()
bi = B()
print id(ai.x) == id(bi.x)
ai.x=3
print id(ai.x) == id(bi.x)
Result:
True
False
Note also, that even if you initialize x in __init__, that due to implementation-defined behavior, immutable objects can be cached and reused, and instances ai and bi still share the 0 object.
class A(object):
def __init__(self):
self.x = 0
class B(A):
pass
ai = A()
bi = B()
print id(ai.x) == id(bi.x)
ai.x=3
print id(ai.x) == id(bi.x)
Result (with CPython 2.7.2):
True
False

Categories