How to list all class properties - python

I have class SomeClass with properties. For example id and name:
class SomeClass(object):
def __init__(self):
self.__id = None
self.__name = None
def get_id(self):
return self.__id
def set_id(self, value):
self.__id = value
def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
id = property(get_id, set_id)
name = property(get_name, set_name)
What is the easiest way to list properties? I need this for serialization.

property_names=[p for p in dir(SomeClass) if isinstance(getattr(SomeClass,p),property)]

import inspect
def isprop(v):
return isinstance(v, property)
propnames = [name for (name, value) in inspect.getmembers(SomeClass, isprop)]
inspect.getmembers gets inherited members as well (and selects members by a predicate, here we coded isprop because it's not among the many predefined ones in module inspect; you could also use a lambda, of course, if you prefer).

Related

MetaClasses to remove duplication of class definitions

I have a few classes defined as below in Python:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, name):
self.name = name
self.items = {}
def __getitem__(self, name):
return self.items[name]
def __setitem__(self, name, item):
self.items[name] = item
class Section:
def __init__(self, name):
self.name = name
self.groups = {}
def __getitem__(self, name):
return self.groups[name]
def __setitem__(self, name, group):
self.groups[name] = group
class List:
def __init__(self, name):
self.name = name
self.sections = {}
def __getitem__(self, name):
return self.sections[name]
def __setitem__(self, name, section):
self.sections[name] = section
The pattern of Group, Section and List is similar. Is there a way in Python using MetaClasses to refactor this to avoid code duplication?
Yes - I'd do it using inheritance as well, but instead of having the specific attribute name defined in __init__, would set it as a class attribute. The base could even be declared as abstract.
class GroupBase():
collection_name = "items"
def __init__(self, name):
self.name = name
setattr(self.collection_name, {})
def __getitem__(self, name):
return getattr(self, self.collection_name)[name]
def __setitem__(self, name, item):
getattr(self, self.collection_name)[name] = item
class Section(GroupBase):
collection_name = "groups"
class List(GroupBase):
collection_name = "sections"
Note that more class attributes could be used at runtime, for example
to specify the item type for each collection, and enforce typing inside __setitem__, if needed.
Or, as you asked, it is possible to literally use a string-template system and just use an "exec" statement inside a metaclass to create new classes.
That would be closer to what "templates" are. The class code itself would live inside a string, and the patterns can use normal strign substitution with .format(). The major difference with C++ templates is that the language runtime itself will do the substitution at runtime - instead of compile (to bytecode) time. The exec function actually causes the text templat to be compiled at this point - yes, it is slower than pre-compiled code, but since it is run just once, at import time, that does not make a difference:
group_class_template = """\
class {name}:
def __init__(self, name):
self.name = name
self.{collection_name} = {{}}
def __getitem__(self, name):
return self.{collection_name}[name]
def __setitem__(self, name, item):
self.{collection_name}[name] = item
"""
class TemplateMeta(type):
def __new__(mcls, name, bases, cls_namespace, template):
# It would be possible to run the template with the module namespace
# where the stub is defined, so that expressions
# in the variables can access the namespace there
# just set the global dictionary where the template
# will be exec-ed to be the same as the stub's globals:
# modulespace = sys._getframe().f_back.f_globals
# Othrwise, keeping it simple, just use an empty dict:
modulespace = {}
cls_namespace["name"] = name
exec(template.format(**cls_namespace), modulespace)
# The class is execed actually with no custom metaclass - type is used.
# just return the created class. It will be added to the modulenamespace,
# but special attributes like "__qualname__" and "__file__" won't be set correctly.
# they can be set here with plain assignemnts, if it matters that they are correct.
return modulespace[name]
class Item:
def __init__(self, name):
self.name = name
class Group(metaclass=TemplateMeta, template=group_class_template):
collection_name = "items"
class Section(metaclass=TemplateMeta, template=group_class_template):
collection_name = "groups"
class List(metaclass=TemplateMeta, template=group_class_template):
collection_name = "sections"
And pasting this in the REPL I can just use the created classes:
In [66]: a = Group("bla")
In [67]: a.items
Out[67]: {}
In [68]: a["x"] = 23
In [69]: a["x"]
Out[69]: 23
In [70]: a.items
Out[70]: {'x': 23}
The major drawback of doing it this way is that the template itself is seem just as a string, and the tooling like linters, static type checkers, auto-complete based in static scannng in IDEs, won't work for the templated classes. The idea could be evolved so that templates would be valid Python code, in ".py" files - they can be read as any other file at import time - one'd just need to specify a templating system other than using the built-in str.format so that templates could be valid code. For example, if one defines that names prefixed and ending with a single underscore are names that will be substituted in the template, regular expressions could be used for the name-replacement insteaf of .format.
You could use inheritance:
class Item:
def __init__(self, name):
self.name = name
class Group(Item):
def __init__(self, name):
super().__init__(name)
self._dict = {}
self.items = self._dict
def __getitem__(self, name):
return self._dict[name]
def __setitem__(self, name, item):
self._dict[name] = item
class Section(Group):
def __init__(self, name):
super().__init__(name)
self.groups = self._dict
class List(Group):
def __init__(self, name):
super().__init__(name)
self.sections = self._dict
Another option that is more similar to a templating method could be to use type to dynamically generate your objects:
def factory(cls_name, collection_name='_data'):
def __init__(self, name):
self.name = name
def __getitem__(self, key):
return eval(f'self.{collection_name}[key]')
def __setitem__(self, key, value):
exec(f'self.{collection_name}[key] = value')
attrs = {
'__setitem__': __setitem__,
'__getitem__': __getitem__,
'__init__': __init__,
collection_name: {}
}
exec(f'{cls_name} = type(cls_name, (), attrs)')
return eval(cls_name)
Item = factory('Item')
Group = factory('Group', 'items')
Section = factory('Section', 'groups')
List = factory('List', 'sections')
g = Group('groupA')
s = Section('section_one')
l = List('list_alpha')
g[1] = 10
s['g'] = g
print(g.items, s.groups, l.sections)
{1: 10} {'g': <main.Group object at 0x7fd87bdfecd0>} {}

How do I define setter, getter for dynamically added attributes

I have a class as follows:
class A:
def __init__(self):
pass
def add_attr(self, name):
setattr(self, name, 'something')
How do I define custom setter, getter for self.name? I cannot use __setattr__, __getattribute__ because that will change the behaviour of add_attr too.
EDIT: the users of this class will add arbitrary number of attributes with arbitrary names:
a = A()
a.add_attr('attr1')
a.add_attr('attr2')
I want custom behavior for only these user added attributes.
Building off #Devesh Kumar Singh’s answer, I would implement it in some way like this:
class A:
def __init__(self):
self.attrs = {}
def __setattr__(self, key, value):
if key in self.attrs:
self.set_attr(key, value)
else:
object.__setattr__(self, key, value)
def __getattribute__(self, key):
if key in self.__dict__.get(attrs, {}):
return self.__dict__['get_attr'](self, key)
return object.__getattribute__(self, key)
def get_attr(self, key):
r = self.attrs[key]
# logic
return r
def set_attr(self, key, value):
# logic
self.attrs[key] = value
def add_attr(self, key, value=None):
self.attrs[key] = value
add_attr is only used to initialise the variable the first time. You could also edit __setattr__ to set all new attributes in the self.attrs rather than self.__dict__
Custom getter and setter logic? That's what a property is made for. Usually these are used to magically mask function calls and make them look like attribute access
class MyDoubler(object):
def __init__(self, x):
self._x = x
#property
def x(self):
return x * 2
#x.setter
def x(self, value):
self._x = value
>>> md = MyDoubler(10)
>>> md.x
20
>>> md.x = 20
>>> md.x
40
>>> md._x
20
But there's no rule saying you can't abuse that power to add custom behavior to your getters and setters.
class A(object):
def __init__(self):
pass
#staticmethod
def default_getter_factory(name):
def default_getter(self):
return self.name
return default_getter
#staticmethod
def default_setter_factory(name):
def default_setter(self, value):
setattr(self, name, value)
return default_setter
def add_attr(self, name, getterfactory=None, setterfactory=None):
private_name = f"_{name}"
if getterfactory is None:
getterfactory = self.__class__.default_getter_factory
if setterfactory is None:
setterfactory = self.__class__.default_setter_factory
getter, setter = getterfactory(private_name), setterfactory(private_name)
getter = property(getter)
setattr(self.__class__, name, getter)
setattr(self.__class__, name, getter.setter(setter))
That said this is all a bit silly, and chances are that whatever it is you're trying to do is a thing that shouldn't be done. Dynamic programming is all well and good, but if I were to review code that did this, I would think very long and hard about alternative solutions before approving it. This reeks of technical debt to me.
One possibility I could think of is to have a dictionary of dynamic attributes, and set and get the dynamic attributes using the dictionary
class A:
def __init__(self):
#Dictionary of attributes
self.attrs = {}
#Set attribute
def set_attr(self, name):
self.attrs[name] = 'something'
#Get attribute
def get_attr(self, name):
return self.attrs.get(name)
a = A()
a.set_attr('var')
print(a.get_attr('var'))
The output will be something
Or an alternate is to use property decorator to add arguments explicitly outside the class, as described here
class A:
def __init__(self):
pass
a = A()
#Add attributes via property decorator
a.attr_1 = property(lambda self: self.attr_1)
a.attr_2 = property(lambda self: self.attr_2)
#Assign them values and print them
a.attr_1 = 4
a.attr_2 = 6
print(a.attr_1, a.attr_2)
The output will be 4 6
I am gonna answer my own question just for reference. This is based on others' answers here. The idea is to use default __setattr__ and __getattribute__ on attributes not added through add_attr.
class A:
def __init__(self):
self.attrs = {}
def add_attr(self, name):
self.attrs[name] = 'something'
def __getattribute__(self, name):
try:
object.__getattribute__(self, 'attrs')[name] # valid only if added by user
# custom logic and return
except (KeyError, AttributeError):
return object.__getattribute__(self, name)
def __setattr__(self, name, val):
# similar to __getattribute__

Python class objectivity convention

Is it a good style to create classes like that ? I read the PEP8 document but I didn't saw any good example. If not how is it a proper way ? Thanks for any answers.
class Zone:
def __init__(self, index=None, name=None):
self._index = index
self._name = name
#property
def index(self):
return self._index
#property
def name(self):
return self._name
#index.setter
def index(self, index):
self._index = index
#name.setter
def name(self, name):
self._name = name
Your setters and getters don't do anything. With your implementation, the user of this class does this:
z = Zone()
z.name = 'foo'
print(z.name)
Compare to this implementation:
class Zone:
def __init__(self, index=None, name=None):
self.index = index
self.name = name
z = Zone()
z.name = 'foo'
print(z.name)
It works exactly the same with a lot less code.
Unless you do anything in your setters and/or getters, you don't need them.
If what you intend doing is encapsulating your data and setting it with setters and getting it with getters, then what you did will not be helpful. you declared the _name and _index as protected, it does not mean it cannot be accessed by extenal functions, so functions outside the class can easily access and change them, making your getter and setter to be useless.
However, you can declare them as private by using one additional underscore in front, so that your property class will be removed and then the setters class will be useful, it will no longer be accessed by external functions.
class Zone:
def __init__(self,index=None,name=None):
self.__index = index
self.__name = name
def index(self, index):
self.__index = index
def name(self, name):
self.__name = name
def get_name(self):
return self.__name
zone=Zone()
zone.name('ben')
print(zone.get_name())
>>>ben
print(zone.__name)
>>> AttributeError: 'Zone' object has no attribute '__name'

How to convert Object with Properties to JSON without "_" in Python 3?

I would like to convert an Python object into JSON-format.
The private attributes of the class User are defined using properties. The method to_Json() I have found here
class User:
def __init__(self):
self._name = None
self._gender = None
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
#property
def gender(self):
return self._gender
#gender.setter
def gender(self, gender):
self._gender = gender
def to_Json(self):
return json.dumps(self, default=lambda o: o.__dict__, allow_nan=False, sort_keys=False, indent=4)
The output using this class and method is:
{
"_name": "Peter",
"_age": 26
}
What is the easiest way to get rid of the underscores in the JSON-format? (I want "name" instead of "_name") Removing the underscore in the class is not an option since I get an error (max. recursion depth). I think renaming the methods of the attributes would solve this problem, but is this the best solution here?
Renaming all keys before the json.dumbs (see here) is not a practical approach because I my class is more complex than the above example.
So, what is the best practice to convert a Python object into JSON-format as fast as possible?
If the example code you posted mirrors your real code, there really isn't any reason for the properties at all. You could just do:
class User(object):
def __init__(self):
self.name = None
self.age = None
since you're not really hiding anything from the user behind the underscores and properties anyway.
If you do need to do the transformation, I like to do it in a custom encoder:
class MyEncoder(json.JSONEncoder):
def default(self, o):
return {k.lstrip('_'): v for k, v in vars(o).items()}
json_encoded_user = json.dumps(some_user, cls=MyEncoder)
In Python, you'd normally not use properties for basic attributes. You'd leave name and age to be directly accessible attributes. There is no need to wrap those in property objects unless you need to transform the data when getting or setting.
If you have good reasons to use attributes with underscores but reflect them as JSON dictionaries, you can transform your dictionary when converting to a dictionary:
object_dict = lambda o: {key.lstrip('_'): value for key, value in o.__dict__.items()}
return json.dumps(self, default=object_dict, allow_nan=False, sort_keys=False, indent=4)
Note that this does nothing to prevent collisions. If you have both a _name and a name attribute on your instance, you'll clobber one or the other.
I had a similar problem, but I had private fields with two underscore characters.
class User:
def __init__(self, id, name):
self.id = id
self.name = name
#property
def id(self):
return self.__id
#id.setter
def id(self, id):
self.__id = id
#property
def name(self):
return self.__name
#name.setter
def name(self, name):
self.__name = name
Therefore, my json encoder is slightly different
from json import JSONEncoder
def beautify_key(str):
index = str.index('__')
if index <= 0:
return str
return str[index + 2:]
class JsonEncoder(JSONEncoder):
def default(self, o):
return {beautify_key(k): v for k, v in vars(o).items()}
Full answer is here.

Mutually Reference-able Instances in Python

Say I have a pair of instances that reference one another mutually. Is there a preferable manner to structure this relationship than the following.
class Human():
def __init__(self, name):
self.name = name
self.pet = Dog('Sparky', self)
def pet(self, animal):
self.pet.receive_petting()
class Dog(Pet):
def __init__(self, name, owner):
self.name = name
self.owner = owner
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
The thing I don't like is that the relationship needs to be specified in two places. Any ideas on how to make this dryer?
I would break this into three classes:
class Human():
def __init__(self, name):
self.name = name
class Dog(Pet):
def __init__(self, name):
self.name = name
def bark_at(self, person):
"do something"
class OwnerPetRelation():
def __init__(self, dog, human):
self.owner=human
self.pet=dog
Now, one owner can also have many dogs, we just need to define as many OwnerPetRelations.
Similarly, a dog can also belong to multiple owners now.
I would create a method on Human that allows you to add pets (since a human might have many pets):
class Human():
def __init__(self, name):
self.name = name
self.pets = []
def add_pet(self, pet):
pet.owner = self
self.pets.append(pet)
def pet(self, animal):
for pet in self.pets:
pet.receive_petting()
class Dog(Pet):
def __init__(self, name):
self.name = name
self.owner = None
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
This can be used as follows
human = Human('Jim')
human.add_pet(Dog('Woof'))
This approach can of course also be used for just a single pet and one could also extend it to allow pets to be owned by many humans.
There's nothing really Python-specific here; this is just a limitation of constructor-based dependency injection. It's hard to inject a reference to another object that cannot have been created yet. Instead, you can create an object that has a reference to something that will have a reference to the other object. For instance, you can pass a function to the constructor that will be able to return the value:
class Human():
def __init__(self,name,dog):
self.name = name
self._dog = dog
#property
def dog(self):
return self._dog()
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
return self._human()
Then you can use it like this:
human = None
dog = Dog('fido',lambda: human)
human = Human('john',lambda: dog)
print(dog.human.name)
print(human.dog.name)
john
fido
It is not hard to update this so that the property function caches the value, of course. E.g.:
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
try:
return self._human_
except AttributeError:
self._human_ = self._human()
return self._human_

Categories

Resources