I'm trying to create a function, that would take a parameter, then make a global variable out of it(more precisely an instance of a class).
class SomeClass():
#some stuff defined inside
def create(crt, **kwargs):
globals()[crt] = SomeClass()
for key, value in kwargs.items():
if crt.__dict__.__contains__(key):
crt.__setattr__(key, value)
return crt
Output that I'm interested in would be:
create(foo, class_attribute=10)
That would then allow me to:
foo.other_attribute = "whatever"
I can't pass a parameter without '' if it's not defined earlier, neither can I pass a string, because it's not a variable in itself, hence it can't be an instance of a class.
Would that be even possible?
This is a bad idea, but here's how to do it.
You need to pass the name as a string. When you're setting the attributes, do it on a local variable that contains the new object.
def create(crt, **kwargs):
obj = SomeClass()
for key, value in kwargs.items():
if obj.__dict__.__contains__(key):
obj.__setattr__(key, value)
globals()[crt] = obj
return obj
create('foo', class_attribute=10)
foo.other_attribute = 'whatever'
Related
I want to use strings as variable names inside a init method of a function, however it does not seem to work so far. I tried the following:
class SomeClass:
def __init__(self, car_brand="BMW", **kwargs):
car_brand = 'Mercedes'
# change car_brand variable with exec
exec("identifier " + "= 'Audi'")
# change car_brand variabel with local
str = "car_brand"
locals()[str] = 'Audi'
self.car_brand = car_brand
sc = SomeClass(car_brand="BMW")
sc.car_brand
My output is "Mercedes" so apparently it is possible to simply overwrite the input argument however it is not possible to overwrite the variable with "Audi" using the string "car_brand" as variable name.
There are multiple ways to accomplish this.
# modifying the globals
globals()[name] = value
from operator import setitem
setitem(globals(), name, value)
# custom dicionary, like globals
custom_dict[name] = value
Perhaps you want to modify only the current instance's attributes?
setattr(self, name, value)
This here may be a bit tricky with no straight forward solution, but I would also be happy with a more complex solution:
I have an instance binding data descriptor binding to a global instance that I want to pass as a function argument without being evaluated (i.e. __get__() executed). In this code example it does not work, and the descriptor passes the current value 10 of type int to the function argument, instead of itself:
class RevealAccess(object):
"""A 'instance binding data descriptor' that sets and returns values
normally and prints a message logging their access.
The `x`-value is stored in the instance dictionary `my_instance.__dict__`.
"""
def __init__(self, init_value=None):
self.init_value = init_value
def __get__(self, instance, owner):
value = getattr(instance, self.__name__ + '_value')
print('I AM A DATA-DESCRIPTOR retrieving `{}` value: {}'.format(self.__name__, value))
return value
def __set__(self, instance, value):
print('I AM A DESCRIPTOR updating `{}` to value: {}'.format(self.__name__, value))
setattr(instance, self.__name__ + '_value', value)
class MyClass(object):
x = RevealAccess(init_value=10)
def __new__(cls):
instance = object.__new__(cls)
for desc_name in [key for key in cls.__dict__.keys() if isinstance(cls.__dict__[key], RevealAccess)]:
cls.__dict__[desc_name].__name__ = desc_name
instance.__dict__[desc_name + '_value'] = cls.__dict__[desc_name].init_value
return instance
my_instance = MyClass()
def func_with_descriptor_as_argument(descriptor_arg):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor_arg))
print('`type(descriptor_arg)`: {}'.format(type(descriptor_arg)))
print('Changing `my_instance.x` value results in:')
descriptor_arg = 5
print('INSIDE the function after changing `my_instance.x` = {}\n\n'.format(descriptor_arg))
if __name__ == '__main__':
print('\n\nOUTSIDE the function `my_instance.x`: {}'.format(my_instance.x))
print('Changing `my_instance.x` value results in:')
my_instance.x = 5
print('OUTSIDE the function after changing `my_instance.x` = {}\n\n'.format(my_instance.x))
print('Reset:')
my_instance.x = 10
func_with_descriptor_as_argument(descriptor_arg=my_instance.x)
The output is:
I AM A DATA-DESCRIPTOR retrieving `x` value: 10
OUTSIDE the function `my_instance.x`: 10
Changing `my_instance.x` value results in:
I AM A DESCRIPTOR updating `x` to value: 5
I AM A DATA-DESCRIPTOR retrieving `x` value: 5
OUTSIDE the function after changing `my_instance.x` = 5
Reset:
I AM A DESCRIPTOR updating `x` to value: 10
I AM A DATA-DESCRIPTOR retrieving `x` value: 10
INSIDE the function `descriptor_arg=my_instance.x`results in: 10
`type(descriptor_arg)`: <class 'int'>
Changing `my_instance.x` value results in:
INSIDE the function after changing `my_instance.x` = 5
I do understand that it does not work this way. But what I want is to manipulate the global instance dictionary value my_instance.__dict__['x_value'] inside the function. I have to repeat this with many instances & functions and the actual descriptors are also doing other stuff (in this example it's only printing "I AM ..." but in my case it's e.g. type checking, triggering other processes etc.), so direct dictionary manipulation is undesired. It has to be done by the descriptor.
Question
Can I build some kind descriptor that could pass a kind of reference to the function argument which behaves equivalently?
So far
I've been looking at different options:
pass my_instance and the string name x separately or as tuple and work with getattr(), setattr() inside the function. I don't like it because it's for a framework and not nice for anybody.
overloading the descriptor, letting it detect if it is passed to a function with e.g. inspect, some AST-package and and then build an appropriate reference inside the overloaded __get__() and return it. I may manage the detection part, but I have no clue how the reference could look like? Wrap it in another descriptor?
In the end the function argument should work inside the function like a directly callable descriptor but getting/setting the global dictionary my_instance.__dict__['x_value'] (and doing all the other stuff mentioned).
I'm happy for any ideas and looking forward to discuss!
I found a quite nice solution, it's a kind-of-class-wrapper that the descriptor-owner-instance my_instance creates and returns in __getattr__() if you try to get a non-existing attribute from it.
Adjusting above example the solution looks like this:
class RevealAccess(object):
"""A 'instance binding data descriptor' that sets and returns values
normally and prints a message logging their access.
The `x`-value is stored in the instance dictionary `my_instance.__dict__`.
"""
def __init__(self, init_value=None):
self.init_value = init_value
def __get__(self, instance, owner):
value = getattr(instance, self.__name__ + '_value')
print('I AM A DATA-DESCRIPTOR retrieving `{}` value: {}'.format(self.__name__, value))
return value
def __set__(self, instance, value):
print('I AM A DESCRIPTOR updating `{}` to value: {}'.format(self.__name__, value))
setattr(instance, self.__name__ + '_value', value)
class DescriptorReference:
def __init__(self, instance, descriptor_name):
self.__dict__['instance'] = instance
self.__dict__['descriptor_name'] = descriptor_name
def __getattr__(self, name):
return object.__getattribute__(self.instance, self.descriptor_name)
def __setattr__(self, dummy, value):
setattr(self.instance, self.descriptor_name, value)
class MyClass(object):
x = RevealAccess(init_value=10)
def __new__(cls):
instance = object.__new__(cls)
for desc_name in [key for key in cls.__dict__.keys() if isinstance(cls.__dict__[key], RevealAccess)]:
cls.__dict__[desc_name].__name__ = desc_name
instance.__dict__[desc_name + '_value'] = cls.__dict__[desc_name].init_value
return instance
def __getattr__(self, name):
return DescriptorReference(instance=self, descriptor_name=self.__class__.__dict__['x'].__name__)
my_instance = MyClass()
def func_with_descriptor_value_as_argument(descriptor_arg):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor_arg))
print('`type(descriptor_arg)`: {}'.format(type(descriptor_arg)))
print('Changeing `my_instance.x` value results in:')
descriptor_arg = 5
print('INSIDE the function after changeing `my_instance.x` = {}\n\n'.format(descriptor_arg))
def func_with_descriptor_as_argument(descriptor):
print('\n\nINSIDE the function `descriptor_arg=my_instance.x`results in: {}'.format(descriptor.x))
print('`type(descriptor_arg)`: {}'.format(type(descriptor.x)))
print('Changeing `my_instance.x` value results in:')
descriptor.x = 5
print('INSIDE the function after changeing `my_instance.x` = {}\n\n'.format(descriptor.x))
if __name__ == '__main__':
x_ref = DescriptorReference(instance=my_instance,
descriptor_name=my_instance.__class__.__dict__['x'].__name__)
print('\n\nOUTSIDE the function `my_instance.x`: {}'.format(my_instance.x))
print('Changeing `my_instance.x` value results in:')
my_instance.x = 5
print('OUTSIDE the function after changeing `my_instance.x` = {}\n\n'.format(my_instance.x))
print('Reset:')
my_instance.x = 10
func_with_descriptor_as_argument(descriptor=my_instance.x_ref)
print('OUTSIDE the function after changeing INSIDE the function `my_instance.x` = {}\n\n'.format(my_instance.x))
Now normally you call my_instance.x to run the descriptor and get/set the value of it's global dictionary entry my_instance.__dict__['x'] and do all the other descriptor stuff/work. By passing my_instance with a non-existing attribute call as argument to the function, e.g. func_...(descriptor=my_instance.x_ref), my_instance calls __getattr_() after not finding the x_ref in its __dict__. Then __getattr__() generates the DecriptorReference() and return it to descriptor argument in the function. Inside the function you have now full descriptor functionality which is manipulating the global dictionary of my_instance. So the call descriptor.x inside the function does absolutely the same as my_instance.x outside the function.
Note: In this implementation you can call any attribute on descriptor and it will be identical to my_instance.x, e.g. descriptor.made_my_day. If this is not desired it can be easily changed with a if name == 'x': before the return in DescriptorReference.__getattr__()
I'm new to classes, this is a small piece of code I've written, but I'm still really shaky on this concept, and am wondering exactly how the method node_name comes into play here and if it's even needed?
from rdflib import BNode
class HigherNode(object):
def node_name(name):
return name
def __init__(self, **kwargs):
self.node_type = kwargs.get('node_type', 'cog_con')
self.position = kwargs.get('position', 0)
self.node_id = self.node_name
self.node = kwargs.get(self.node_name(), BNode())
for key, value in kwargs.items():
setattr(self, key, value)
def __str__(self):
return 'This is the node of {} in the graph'.format(self.node_id)
I behavior that I'm seeking is something equivalent to this:
elephant = BNode()
when used as:
some_node = HigherNode(node_id = 'elephant')
So, first off, methods have to be called by an instance of the class. So, your behavior would look something like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name()
However, you never declared name inside the class. So, you'll have to do something like this:
def node_name(self):
return self.name
(All instances pass a reference to themselves to thier functions when called, so you'll always have to have at least one variable in the function call. You don't have to call it self.)
Really, it looks like what you want is actually a name setter/getter.
Try this:
Declare/set the variable in __init__.
def __init__(self, **kwargs):
self.node_name= kwargs.get('node_name', None)
Then you can use the variable like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
Since your class extends object, use getter/setter properties.
#property
def node_name(self):
return self.node_name
#node_name.setter
def node_name(self, x):
self.node_name = str(x)
These are called exactly the same as above in option 1:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
I prefer this method, since it allows you much more control over how things are set, or even whether or not you can set or get them! (Just make a getter property without a corresponding setter property, for instance.)
However, this second method is more work to set up and may not be suitable for simple variables.
I have following two code samples
Example 1:
class MyClass(object):
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
m = MyClass('ten',10)
print m._dict
Output:
AttributeError: 'MyClass' object has no attribute '_dict'
Example2:
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
m = MyClass('ten',10)
print m._dict
Output:
None
I am quite surprised with above behavior
Why the example2 compiled successfully by just addition of _dict = {}
line, and line present at class scope.
also why None output?
I believed class scope variables has no relation with instance variable
(special with self)
Any Explaination?
Your 'example 2' defines a single dictionary at the class level. All instances of the class will share that same dictionary, at least unless you reassign _dict on the instance.
See this question for a detailed explanation:
Why do attribute references act like this with Python inheritance?
As for why you're getting None - the update method changes its dict in place, and returns None.
The None output is because dict.update returns None. It modifies the dictionary itself, but does not return anything. So you probably wanted self._dict.update({key:value}). However, self._dict doesn't exist at initialization. So it would make more sense to do self._dict = {key: value}. If you're trying to modify the object's internal dictionary, then you should do self.__dict__.update({key:value}). However, this is bad practice. A better idea would be to write setattr(self, key, value). The reason Example2 is working successfully is because if you try to do getattr(instance, thing) (which is what instance.thing does), and thing is not in instance.__dict__, then instance.__class__.__dict__ will be checked instead.
Because the _dict in Example 2 is a class variable so it's an attribute of MyClass where as the _dict in Example 1 is an instance variable so it's a instance attribute.
Example 1: you are trying to update an object that is yet to be created. therefore error.
Example 2: When working in the inner scope of the function, if you modify the variable it makes changes to the previously defined _dict. But if you assign the value, it makes a new variable with the same name in the inner scope.
This will work.
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict.update({key:value})
This will not.
class MyClass(object):
_dict = {}
def __init__(self, key, value):
self._dict = self._dict.update({key:value})
because you are doing an assigning operation. It makes a new variable. So no changes are made to the _dict in the outer scope. Your _dict in the outer scope is still empty and returns None.
self._dict does not yet exist, so the first version raises that exception. The second one actually falls through looking _dict up on the instance and instead updates the class attribute, then assigns the class-level dictionary to the instance-scope _dict attribute.
I want to understand python metaclasses. For practice I'm implementing a declarative way for writing classes (similar to sqlalchemy.ext.declarative). This looks promising as long as I only have one attribute.
But when I add another attribute, some part of the first attribute is changed and the value of the first attribute is validated against the pattern of the second attribute. This might be caused by the metaclass, by a closure, by the property or a combination of them. I try to give a minimal, complete but readable example.
#! /usr/bin/env python
"""
Something like:
class Artist:
locale = Pattern('[A-Z]{2}-[A-Z]{2}')
should be equivalent to:
class Artist:
def __init__(self):
self._locale = None
#property
def locale(self):
return self._locale
#locale.setter
def locale(self, value):
validate(value, '[A-Z]{2}-[A-Z]{2}')
self._locale = value
Problem:
The code below works if Artist has only one attribute.
When I add another one with a different pattern, only that last
pattern is used in validation.
"""
import re
import unittest
# this class (and future siblings) are used to describe attributes
class Pattern(object):
def __init__(self, pattern):
self.pattern = pattern
def validate(self, value):
if value is None:
return
if not re.match("^%s$" % self.pattern, value):
raise ValueError("invalid value: %r" % value)
def __repr__(self):
return "%s(pattern=%r)" % (self.__class__.__name__, self.pattern)
# __metaclass__ based class creation
def createClassFromDeclaration(name, bases, dct):
""" Examine dct, create initialization in __init__ and property. """
attributes = dict()
properties = dict()
for key, value in dct.iteritems():
if not isinstance(value, Pattern):
continue
pattern = value
pattern.attribute = "_%s" % key
attributes[key] = pattern
def fget(self):
return getattr(self, pattern.attribute)
def fset(self, value):
pattern.validate(value)
return setattr(self, pattern.attribute, value)
properties[key] = property(fget, fset)
def __init__(self, **kwargs):
# set all attributes found in the keyword arguments
for key, value in kwargs.iteritems():
if key in self.__attributes__:
setattr(self, key, value)
# set all attributes _NOT_ found to None
for key, declaration in attributes.iteritems():
if not hasattr(self, declaration.attribute):
setattr(self, key, None)
dct = dict(dct)
dct.update(properties)
dct['__init__'] = __init__
dct['__attributes__'] = attributes
return type(name, bases, dct)
# declarative class
class Artist(object):
__metaclass__ = createClassFromDeclaration
# FIXME: adding a second attribute changes the first pattern
locale = Pattern('[A-Z]{2}-[A-Z]{2}')
date = Pattern('[0-9]{4}-[0-9]{2}-[0-9]{2}')
# some unit tests
class TestArtist(unittest.TestCase):
def test_attributes_are_default_initialized(self):
artist = Artist()
self.assertIsNone(artist.date)
self.assertIsNone(artist.locale)
def test_attributes_are_initialized_from_keywords(self):
artist = Artist(locale="EN-US", date="2013-02-04")
self.assertEqual(artist.date, "2013-02-04")
# FIXME: the following does not work.
# it validates against the date pattern
self.assertEqual(artist.locale, "EN-US")
def test_locale_with_valid_value(self):
artist = Artist()
artist.date = "2013-02-04"
self.assertEqual(artist.locale, "2013-02-04")
# FIXME: the following does not work.
# it validates against the date pattern
artist.locale = "EN-US"
self.assertEqual(artist.locale, "EN-US")
def test_locale_with_invalid_value_throws(self):
artist = Artist()
with self.assertRaises(ValueError):
artist.locale = ""
with self.assertRaises(ValueError):
artist.locale = "EN-USA"
if __name__ == '__main__':
unittest.main()
# vim: set ft=python sw=4 et sta:
When I comment out the second attribute ('date') the tests succeed, but with the second attribute the tests that try to set the first attribute ('locale') fail. What causes the unittests to fail?
Disclaimer: This code is only for training. There are ways to create the same functionality that do not involve metaclasses, properties and closures (as you and I know). But we don't learn anything new if we only walk the streets we know. Please help me expand my Python knowledge.
The problem doesn't really have anything to do with metaclasses or properties per se. It has to do with how you're defining your get/set functions. Your fget and fset reference the variable pattern from the enclosing function. This creates a closure. The value of pattern will be looked up at the time fget/fset are called, not at the time they're defined. So when you overwrite pattern on the next loop iteration, you cause all fget/fset functions to now reference the new pattern.
Here's a simpler example that shows what's going on:
def doIt(x):
funs = []
for key, val in x.iteritems():
thingy = val + 1
def func():
return thingy
funs.append(func)
return funs
>>> dct = {'a': 1, 'b': 2, 'c': 3}
>>> funs = doIt(dct)
>>> for f in funs:
... print f()
3
3
3
Notice that, even though the three functions are defined at times when thingy has different values, when I call them later they all return the same value. This is because they are all looking up thingy when they're called, which is after the loop is done, so thingy just equals the last value it was set to.
The usual way to get around this is to pass in the variable you want to close over as the default value of an additional function argument. Try doing your getter and setter like this:
def fget(self, pattern=pattern):
return getattr(self, pattern.attribute)
def fset(self, value, pattern=pattern):
pattern.validate(value)
return setattr(self, pattern.attribute, value)
Default arguments are evaluated at function definition time, not call time, so this forces each function to "save" the value of pattern it wants to use.