I have a very simple class that defines properties like:
class Person:
fields = set()
#property
def id(self):
self.fields.add('id')
return 'person.id'
The idea is for this class to record which properties have been accessed. Now the problem comes when I need to start supporting: person.metadata.key where metadata is basically an HStore object or 1 level JSON, the key is arbitrary and the idea is for the class Person so record access to any of the keys in metadata. I tried something like this:
class CustomerBulkContext:
fields = set()
class PersonMetadata:
def __getitem__(self, attr):
fields.add(f'metadata.{attr}')
return f'person.metadata.{attr}'
metadata = CustomerMetadataContext()
Now obviously the problem is that fields inside PersonMetadata is not a known variable at this point. How can I overcome this issue, I don't know if it's possible to do in Python without too much code.
In order to track accesses like person.metadata.key you'd have to have person.metadata return an object which itself tracks how it was accessed. In order to track the whole chain, you'd pass the chain so far to the object.
Something like:
class AccessTracker:
def __init__(self, path=()):
self._path = path
def __getattr__(self, name):
print('Accessed %s in %s' % (name, self._path))
return AccessTracker(self._path + (name,))
Related
So I have a very simple "Customer" class which only stores the name and creates an id:
import secrets
class Customer:
def __init__(self, name):
self.name = name
def get_id(self):
id = secrets.token_hex(20)
return id
In another class my objective is to create a list where all the instances of Customer are stored and in all the functions in the class I need to handle either the name or the id of those customers.
The two methods I've found so far are import gc and do something like this
for i in gc.get_objects():
if isinstance(i, Customer):
customer_list.append(i)
which I really dont understand and don't know if it works.
The other way I've found is to use getmembers(), but again I still have no idea about the syntax in using this function and how the name and the id will be stored.
Thanks in advance for any help!
a) In the following example I demonstrate a way to store object instances in a class variable (list) and access them throught that list. b) I think that your method get_id returns different id every time is called, so considering that you want only one id per customer I suggest to create the id inside __init__ and store it in a property.
import secrets
class Customer:
objects = []
def __init__(self, name):
self.name = name
self.id = secrets.token_hex(20)
Customer.objects.append(self)
Customer('A test')
Customer('B test')
Customer('C test')
for c in Customer.objects:
print(c.name, c.id)
I'm trying to find the best for users of my python library to implement an abstract class I wrote.
Namely, my abstract class define an API to access specific values stored in a database, but I would like to let the user choose how to store it (simple text file, json, sqlite, etc.)
My problem is, how should I retrieve the class the user create and use it in my library ?
This is the solution I came up with, but I don't find it very graceful and wonder if there is a more pythonic way.
In my library:
from abc import ABC, abstractmethod
class Database(ABC):
#abstractmethod
def get(self, index):
pass
#abstractmethod
def insert(self, data):
pass
def get_database():
"""call this anywhere I need a concrete database class"""
return Database.__subclasses__()[-1]
In the user code
class SqliteDatabase(Database):
def get(self, index):
# sqlite SELECT and such
return data
def insert(self, data):
# sqlite INSERT INTO
# return data with index provided
return data
Of course, I will return a better error than IndexError if there is no subclass defined, but you get the idea.
Thank you in advance !
I finally settled for something else, as Blorgbeard suggested
_databases = {}
def register(dbname="default"):
def wrapper(klass):
_databases[dbname] = klass
return klass
return wrapper
def get_db(name="default"):
return _databases[name]
And the user only needs to declare
#register()
class SqliteDatabase:
def __get__(self, index):
# retrieve data
if data is None:
raise KeyError(index)
return data
This way, anybody can declare as many as databases as they want.
If you have improvements over this version, I'll gladly take them.
If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.
I'm making Django like ORM for my study project and because we are not allowed to use existing ORMs (If you want to use one you have to code it yourself) and just for educating myself, i thought that the same kind of ORM like in Django would be nice.
In the ORM I wan't to make model definitions in same style that they are implemented in Django. ie.
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Django uses metaclasses and in my project I'm using too, but I have problem with the fact that metaclasses construct classes not instances and so all attributes are class attributes and shared between all instances.
This is generic example what I tried but because of what I earlier said, it won't work:
def getmethod(attrname):
def _getmethod(self):
return getattr(self, "__"+attrname).get()
return _getmethod
def setmethod(attrname):
def _setmethod(self, value):
return getattr(self, "__"+attrname).set(value)
return _setmethod
class Metaclass(type):
def __new__(cls, name, based, attrs):
ndict = {}
for attr in attrs:
if isinstance(attrs[attr], Field):
ndict['__'+attr] = attrs[attr]
ndict[attr] = property(getmethod(attr), setmethod(attr))
return super(Metaclass, cls).__new__(cls, name, based, ndict)
class Field:
def __init__(self):
self.value = 0;
def set(self, value):
self.value = value
def get(self):
return self.value
class Mainclass:
__metaclass__ = Metaclass
class Childclass(Mainclass):
attr1 = Field()
attr2 = Field()
a = Childclass()
print "a, should be 0:", a.attr1
a.attr1 = "test"
print "a, should be test:", a.attr1
b = Childclass()
print "b, should be 0:", b.attr1
I tried to lookup from Djangos source but it is too complicated for me to understand and the "magic" seems to be hidden somewhere.
Question is simple, how Django does this in very simplificated example?
The answer is quite simple really, once you check the right code. The metaclass used by Django adds all fields to <model>._meta.fields (well, kinda), but the field attribute is removed from the actual class. The only exception to this is a RelatedField subclass, in which case an object descriptor is added (similar to a property - in fact, a propery is an object descriptor, just with a native implementation).
Then, in the __init__ method of the model, the code iterates over all fields, and either sets the provided value in *args or **kwargs, or sets a default value on the instance.
In your example, this means that the class Person will never have attributes named first_name and last_name, but both fields are stored in Person._meta.fields. However, an instance of Person will always have attributes named first_name and last_name, even if they are not provided as arguments.
I'm trying to use properties and I tried to change python documentation's code. I'd expect the following would print anything, but it doesn't. Why does it not print anything?
class User:
def getter(self, name):
def get_prop(self):
print 'Getting {}'.format(name)
return getattr(self, name)
return get_prop
def setter(self, name):
def set_prop(self, value):
print 'Setting {} to {}'.format(name, value)
return setattr(self, name, value)
return set_prop
user_id = property(getter, setter)
u = User()
u.user_id = 10
u.user_id
There are two reasons your property doesn't work:
You need to use a new style class (by basing your class on object); you cannot use a property with a setter otherwise (only a getter is supported for old-style classes).
you are generating accessors as nested functions; you need to call those outer methods to generate those accessors, the property() function will no do this for you. As such, you can move those functions out of the class and use them as plain functions instead.
The following code works:
def getter(name):
def get_prop(self):
print 'Getting {}'.format(name)
return getattr(self, name)
return get_prop
def setter(name):
def set_prop(self, value):
print 'Setting {} to {}'.format(name, value)
return setattr(self, name, value)
return set_prop
class User(object):
user_id = property(getter('_user_id'), setter('_user_id'))
Note that I used _user_id for the property 'name' here, otherwise the getattr(self, name) call will trigger an infinite recursion; u.user_id would trigger a getattr(u, 'user_id') which triggers the property again.
Probably because properties only work with new-style objects. Change your class statement to:
class User(object):
Looking further, your getter and setter functions are returning functions, that you then do not call.
You seem to want dynamic attribute names, yet you have just one attribute name returned from property, in your case this is user_id. What is it you are trying to achieve?