How to set `__class__.__name__` as class attribute? - python

I need to get class name from class:
class Cls:
notation = None
def __init__(self):
notation = self.__class__.__name__
print(Cls.notation) prints None but I need 'Cls'
How to fix it or how to define class attribute which returns a name of class?

You are assigning to a local variable, not the class attribute:
def __init__(self):
Cls.notation = self.__class__.__name__
Note that self.__class__ isn't necessarily Cls, if there is a subclass of Cls involved. You might want to use
def __init__(self):
type(self).notation = self.__class__.__name__
depending on your use case.
Assigning to self.notation won't work, because that creates an instance attribute that shadows the class attribute.
If you want Cls.notation == "Cls" immediately after the class is defined, you may as well just hard-code it:
class Cls:
notation = "Cls"
or
class Cls:
pass
Cls.notation = Cls.__name__
though you can also write
class Cls:
notation = __qualname__
to set its value based on the name used in the first line of the statement, though __qualname__ takes into account nesting as well:
class Cls1:
class Cls2:
notation = __qualname__ # "Cls1.Cls2", not "Cls2"

Related

Does function know about the class before binding

Is there a way to access a class (where function is defined as a method) before there is an instance of that class?
class MyClass:
def method(self):
print("Calling me")
m1 = MyClass.method
instance = MyClass()
m2 = instance.method
print(m2.__self__.__class__) # <class 'MyClass'>
# how to access `MyClass` from `m1`?
For example I have m1 variable somewhere in my code and want to have a reference to MyClass the same way I can access it from bound method m2.__self__.__class__.
print(m1.__qualname__) # 'MyClass.method'
The only option I was able to find is __qualname__ which is a string containing name of the class.
The attribute __self__ itself is annotated by Python when the function is bound to an instance and become a method. (The code to that is run somewhere when running the __get__ code in the function, but passing an instance different than None).
So, as people pointed out, you have the option of getting the classname as a string by going through __qualname__. Otherwise, if the functions/methods for which you will need this feature are known beforehand, it is possible to create a decorator that will annotate their class when they are retrieved as a class attribute (in contrast to the native annotation which only takes place when retrieving then as an instance attribute):
class unboundmethod:
def __init__(self, func, cls):
self.__func__ = func
self.class_ = cls
self.__self__ = None
def __call__(self, instance, *args, **kw):
if not isinstance(instance, self.class_):
# This check is actually optional fancy stuff, since we are here! :-)
raise TypeError(f"First parameter fo {self.__func__.__name__} must be an instance of {self.class_}")
return self.__func__(instance, *args, **kw)
def __repr__(self):
return f"Unbound method {self.__func__!r} related to {self.class_}"
class clsbind:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
# the function is being retrieved from the class:
return unboundmethod(self.func, owner)
# return control to usual method creation codepath:
return self.func.__get__(instance, owner)
class MyClass:
#clsbind
def method(self):
print("Calling me")
And on the REPL you can have this:
In [136]: m1 = MyClass.method
In [137]: m1.class_
Out[137]: __main__.MyClass
In [138]: m1(MyClass())
Calling me
You can get the class instance using the __qualname__
my_class = eval(m1.__qualname__.split('.')[-2])
print(my_class)
Not the most generic and safest approach, but should work for this simple scenario.

How to overwrite self after reading yaml? [duplicate]

I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>

Python: Only allow attributes to be set that have a #property decorator

class MyClass():
def __init__(self):
self.attribute_1 = "foo"
self.attribute_2 = "bar"
#property
def attribute_1(self):
return self._attribute_1
#attribute_1.setter
def attribute_1(self,s):
self._attribute_1 = s
#property
def attribute_2(self):
return self._attribute_2
#attribute_2.setter
def attribute_2(self,s):
self._attribute_2 = s
>>> ob = MyClass()
>>> ob.attribute_1 = 'fizz' #Ok
>>> ob.atribute_1 = 'buzz' #want to throw an exception because this has no setter or #property def
I would like my class to complain if we try and set an attribute that has not been decorated with property and a setter. I have tried using slots but can't get it working with the property decorator. 'attribute' in __slots__ conflicts with class variable
Any thoughts?
__slots__ should contain all instance variables, in your case it is _attribute_1 and _attribute_2 (the ones with underscores used internally) so just do that:
class MyClass():
__slots__ = ["_attribute_1", "_attribute_2"]
pass # rest of implementation
note that if your property is just directly forwarding you might as well just put the public variables in the slots and only have properties for fields that need more validation or other logic. having slots is effectively a property really:
>>> MyClass._attribute_1
<member '_attribute_1' of 'MyClass' objects>

Python: how to share class attribute between derived classes?

I want to share some information between all the instances of some class and all it's derived classes.
class Base():
cv = "some value" # information I want to share
def print_cv(self, note):
print("{}: {}".format(note, self.cv))
#classmethod
def modify_cv(cls, new_value):
# do some class-specific stuff
cls.cv = new_value
class Derived(Base):
pass
b = Base()
d = Derived()
b.print_cv("base")
d.print_cv("derived")
Output is as expected (instances of both classes see correct class attribute):
base: some value
derived: some value
I can change the value of this class attribute and everything is still fine:
# Base.cv = "new value"
b.modify_cv("new value")
b.print_cv("base") # -> base: new value
d.print_cv("derived") # -> derived: new value
So far so good. The problem is that the "connection" between Base and Derived classes can be broken if I access cv via derived class:
# Derived.cv = "derived-specific value"
d.modify_cv("derived-specific value")
b.print_cv("base") # -> base: new value
d.print_cv("derived") # -> derived: derived-specific value
This behavior is expected, but this is not what I want!
I understand why a and b see different values of cv - because they are instances of different classes. I have overridden cv value in derived class and now derived class behaves differently, I've used this feature many times.
But for my current task I need a and b always use the same cv!
UPDATE
I have updated the question and now it better describes the real-life situation. Actually I did not modify cv value like this:
Base.cv = "new value"
modifications were done in some classmethods (actually all these class methods were implemented in Base class).
And now solution became obvious, I just need to modify the method slightly:
class Base():
#classmethod
def modify_cv(cls, new_value):
#cls.cv = new_value
Base.cv = new_value
Thank you all for discussion and ideas (in the begining I was going to use getters/setters and module-level attribute)
classmethod is useful when you need to know which class is calling the method, but if you want the same behaviour regardless of the class that's calling the method, you could use staticmethod instead. You can then access the class variable simply through the base class's name with Base.cv:
class Base:
cv = "some value" # information I want to share
def print_cv(self, note):
print("{}: {}".format(note, self.cv))
#staticmethod
def modify_cv(new_value):
Base.cv = new_value
You can still call it on any instance or subclass, but it always changes Base.cv:
>>> b = Base()
>>> d = Derived()
>>> Base.cv == Derived.cv == b.cv == d.cv == "some value"
True
>>> d.modify_cv("new value")
>>> Base.cv == Derived.cv == b.cv == d.cv == "new value"
True
Update:
If you still need access to the class for other reasons, use classmethod with the cls argument as you did before, but still access the base class's variable through Base.cv rather than cls.cv:
#classmethod
def modify_cv(cls, new_value):
do_stuff_with(cls)
Base.cv = new_value
You have to override __setattr__ on the class of the class, i.e. a metaclass:
class InheritedClassAttributesMeta(type):
def __setattr__(self, key, value):
cls = None
if not hasattr(self, key):
# The attribute doesn't exist anywhere yet,
# so just set it here
cls = self
else:
# Find the base class that's actually storing it
for cls in self.__mro__:
if key in cls.__dict__:
break
type.__setattr__(cls, key, value)
class Base(metaclass=InheritedClassAttributesMeta):
cv = "some value"
class Derived(Base):
pass
print(Derived.cv)
Derived.cv = "other value"
print(Base.cv)
Using metaclasses is often overkill, so directly specifying Base might be better.
To avoid unwanted side effects with this solution, consider checking first if key is in some predefined set of attribute names before changing the behaviour.
In Python, inside a method, you can use the bare __class__ variable name to mean the actual class the method is defined in.
This differs from the cls arg that is passed to classmethods, or self.__class__ on regular methods, that will refer to a subclass if the method is invoked in a subclass. Thus, cls.attr = value would set the value on the subclass class' __dict__, and the attribute value will be independent on that subclass from that point on. This is what you are getting there.
Instead, you can use:
class MyClass:
cv = "value"
#classmethod # this is actually optional
def modify_cv(cls, new_value):
__class__.cv = new_value
__class__ is created automatically in Python 3 by
the mechanism that allows one to write
parameterless form of super

How can I reference the __init__ method of a derived class in Python from the base class?

Following the SO questions What is a clean, pythonic way to have multiple constructors in Python? and Can you list the keyword arguments a Python function receives? I want to create a base class that has a from_yaml classmethod, and which also removes unneeded keyword args as shown below. But I think I need to reference the derived class's __init__ method from the base class. How do I do this in Python?
def get_valid_kwargs(func, args_dict):
valid_args = func.func_code.co_varnames[:func.func_code.co_argcount]
kwargs_len = len(func.func_defaults) # number of keyword arguments
valid_kwargs = valid_args[-kwargs_len:] # because kwargs are last
return dict((key, value) for key, value in args_dict.iteritems()
if key in valid_kwargs)
class YamlConstructableClass(object):
#classmethod
def from_yaml(cls, yaml_filename):
file_handle = open(yaml_filename, "r")
config_dict = yaml.load(file_handle)
valid_kwargs = get_valid_kwargs(AnyDerivedClass.__init__, config_dict) # I don't know the right way to do this
return cls(**valid_kwargs)
class MyDerivedClass(YamlConstructableClass):
def __init__(self, some_arg, other_arg):
do_stuff(some_arg)
self.other_arg = other_arg
derived_class = MyDerivedClass.from_yaml("my_yaml_file.yaml")
You already have a reference to the correct class: cls:
valid_kwargs = get_valid_kwargs(cls.__init__, config_dict)
The class method is bound to the class object it is being called on. For MyDerivedClass.from_yaml(), cls is not bound to parent class but to MyDerivedClass itself.

Categories