python - Extract attributes having setter properties in an object - python

I have an object. Using __dict__, I can list down all the attributes of the object. Using __dir__, I can list down all the methods and attributes of the object. Is there a way to list down the attributes having setter properties declared in the class whose instance the object is.

Use:
isinstance(getattr(type(object), 'attribute'), property)
Thanks to #a_guest and OP Rahul Nahata's own prior edit to the question.

To check if an object is a property, use isinstance(value, property).
To list all the attributes and methods of a class, use: vars(MyClass).
Combine everything and get:
all_property_names = [name for name, value in vars(MyClass).items() if isinstance(value, property)]
With the following class,
class MyClass():
def __init__(self):
self._x = 1
#property
def x(self):
return self._x
def notproperty(self):
return self.x
you would get:
# all_property_names
['x']

Related

How do I programmatically create class methods in Python?

Suppose I want to define a Python class whose instances have several members of similar form:
class Bar:
def __init__(self):
self.baz=3
self.x=1
self.name=4
I can instead create all members at once using explicit manipulation of __dict__:
class Bar:
def __init__(self):
self.__dict__.update({name: len(name) for name in ("baz", "x", "name")})
However, if the associated members are class data members, instead of instance members, then I am aware of no analogous way to programmatically mutate Bar.__dict__:
class Bar:
#Fails with "AttributeError: 'mappingproxy' object has no attribute 'update'"
Bar.__dict__.update({name: len(name) for name in ("baz", "x", "name")})
In Javascript, programmatically manipulating the properties of a constructor is possible, because constructors are just ordinary functions, which are just ordinary objects, and therefore just a mutable dictionary mapping properties to more objects. Is it possible to write analogous code in Python?
I guess you can use a metaclass, a base class with __init_subclass__ or use eval to create your class. Here's an example using a base class:
class Base:
attrs_to_set: set[str] = set()
def __init_subclass__(cls) -> None:
for attr in cls.attrs_to_set:
setattr(cls, attr, cls.get_attr_value(attr))
#classmethod
def get_attr_value(cls, attr_name: str):
# default, you can change it depending on your use case i guess.
return len(attr_name)
class Bar(Base):
attrs_to_set = {"x", "baz", "name"}
print(Bar.x, Bar.name, Bar.baz)
# prints: 1 4 3

Python: Find Instance of a class by value

i created much instances of a class. Then I want to find one instance by its name. But I get the error message TypeError: get() missing 1 required positional argument: 'value'.
class Test(object):
def __init__(self, value):
self.value = value
def get(self, value):
if self.value == value:
return self
else:
return None
test_obj = Test('foobar')
print(test_obj.value)
instance = Test.get('foobar')
if instance:
print(instance.value)
Re-reading your question again, I think all of us have missed the point so far. You wanted to check all instances of the class Test to see if an instance has the value 'foobar' (in this case, test_obj. Referencing this answer, you can modify your code like so:
class Test(object):
# class attribute to keep track of class instances
instances = []
def __init__(self, value):
self.value = value
Test.instances.append(self)
# class method to access the get method without any instance
#classmethod
def get(cls, value):
return [inst for inst in cls.instances if inst.value == value]
You can then create multiple tests:
test1 = Test(1)
test2 = Test(2)
test3 = Test(3)
instance = Test.get(3)
# [<__main__.Test object at 0x03F29CD0>]
instance[0].value
# 3
It makes sense for me to return a list of instances instead of one single instance. If you however is only interested in the first match, you can modify the return statement accordingly.
Original answer:
instance = Test.get('foobar') is the problem. You're referencing Test by its class, not its instance. So naturally the instance method .get(self, value) is looking for the first argument self for the instance.
Usually if you already have an instance (e.g. Test().get('foobar')) then the instance is passed into the instance method as self by default.
You could still call the instance method, but you just need to explicitly pass the instance in:
Test.get(test, 'foobar')
The get method belongs to the instance, not the class. A class does not know its instances (you could do some dirty tricks to let the class know, but don't). What you need is a list of instances.
One way you could do it
override the equality operator
create a list of the instances
So, something like
class Test:
def __eq__(self, value):
return self.value == value
test_list = [Test(5), Test(2), Test(3)]
position = test_list.index(2)
The "get" method of your class is not a class method therefore you have to call it on the instance:
test.get("foobar")
However if you modify the method to a classmethod than it won't be able to access the instance attributes only the class atributes will be visible.
For a quick solution I think you should store all of the instances in a datastructure (for example in a list) and you can create a function which will loop through the instances and returns the correct one.

class property that can be accessed and set by all instances (also of subclasses)

I'd like to set a class property that can be shared by all instances of the class or its subclasses. It should be possible for any instance to also set the property.
I tried the following:
class A:
x = 1
#classmethod
def setX(cls, val):
if cls.__bases__:
cls = cls.__bases__[-1]
cls.x = val
This seems to work fine in case of single inheritance. But if I use multiple inheritance, depending on the order of inheritance, it either works or doesn't (i.e., class A is not always the last of bases).
Any ideas for a robust implementation?
Use lexical scoping:
class A(object):
x = 1
#classmethod
def setX(cls, val):
A.x = val
The right way, IMO, is to go with a class property. Since properties are tied to an object's classes __dict__, as opposed to the object's very own __dict__, and since your object happens to be a class, you must attach it to the classes class, its metaclass:
class A(object):
_x = None
class __metaclass__(type):
#property
def x(cls):
return A._x
#x.setter
def x(cls, val):
A._x = val
class B(A):
pass
A.x = 'jim'
B.x = 'joe'
print A.x, B.x
Result:
joe joe
Also, your classes must be new style classes, i.e. they must inherit from object in Python 2.x. And metaclasses are defined differently in Python 3.x:
class MetaA(type):
""" like ___metaclass__ above """
class A(metaclass=MetaA):
_x = None
...

Get class and object attributes of class without methods and builtins

Say I have this class:
class MyClass(object):
my_attrib = 'foo'
my_other_attrib = 'bar'
def mymethod():
pass
Now how can I get ONLY the attributes of the class MyClass, WITHOUT methods and builtins like __dict__ and so on?
I want to get a dictionary like {'my_attrib':'foo', 'my_other_attrib':'bar'}, when applied to the class above.
You can filter out everything you don't need from __dict__:
def getAttributes(clazz):
return {name: attr for name, attr in clazz.__dict__.items()
if not name.startswith("__")
and not callable(attr)
and not type(attr) is staticmethod}
Edit: An alternative that behaves slightly differently for class properties and descriptors:
def getAttributes2(clazz):
attrs = {}
for name in vars(clazz):
if name.startswith("__"):
continue
attr = getattr(clazz, name)
if callable(attr):
continue
attrs[name] = attr
return attrs
(In practice, this should be rarely different from the first version.)
This should get you close:
import inspect
class MyClass(object):
my_attrib = 'foo'
my_other_attrib = 'bar'
def mymethod():
pass
for name, value in inspect.getmembers(MyClass):
if not inspect.ismethod(value) and not name.startswith('__'):
print name
This outputs:
my_attrib
my_other_attrib
NOTE - There may be a better / more-official way to do this, but this should point you in the right direction.
__dict__ gives you all that but you could use a C extension maybe to get what you want. Not sure why you would do that though.
You can use types (doc) to distinguish between members of __dict__.
You can use the builtin dir() to get everything, then filter. You will not need the inspect module.
def get_attrs_without_methods(klass):
attrs = dir(klass)
d = {}
for x in attrs:
if x.startswith('__'): continue
value = getattr(self,x)
if not callable(value):
d[x] = value
return d
Sometimes, you may want to get ONLY class variables instead of class variables AND instance variable.
You can filter out instance variables by relying on __dict__. Or you can get the attributes using __class__ and filter out the methods. __class__ does not return instance variables.
#after collecting your attributes using the above example...
for attr, value in vars(obj).items():
d.pop(attr) #remove instance variables from the dict
#both vars(obj).items() and obj.__dict__.items() return similar iterable.
Note that if the object implementation overrides __dict__ and returns None, vars(obj) and obj.__dict__.items() will not return a dictionary.

Is it safe to replace a self object by another object of the same type in a method?

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>

Categories

Resources