python: passing variable name to a class - python

I have a python class defined in a module1.py file:
class MBVar():
def __init__(self, var_type, expression):
self.var_type = var_type
self.expression = expression
... ecc ...
I would like to be able to write in a main *.py file:
from module1 import MBVar
X = MBVar('integer', 6)
and add to my MBVar class:
self.name = ???
in such a way that: self.name = 'X'.
Is it possible to do this??
Thanks

So I Assume you want to pass variable name and value as parameter and assign it to an object, to do that we don't need the type of the variable since python uses duck typing we just have to add the string representation of the variable name in the inbuilt dictionary __dict__ as key and the integer as value.
class MBVar():
def __init__(self, var_name, expression):
self.__dict__[var_name] = expression
def add_later(self, var_name, expression):
self.__dict__[var_name] = expression
def get_name(self):
return self.name
X = MBVar('name', 6)
print X.get_name() # prints 6
X.add_later('secint',4);
print X.secint #prints 4
X.__dict__['thirdint'] = 7
print X.thirdint #prints 7

I have a solution but i don't think that this is a very good coding practice. Moreover, it is a 2 steps process: it can't be done inside the __init__ method because till the end of this method, the object has not been yet associated to a variable.
class Foo:
def __init__(self):
self.__name = ""
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
if __name__ == "__main__":
a = Foo()
b = Foo()
c = Foo()
dict_v = locals()
v = ""
# this line initialize the variable of name "v" because of its entry in the locals() dict
#-> prevent "RuntimeError: dictionary changed size during iteration "
for v in dict_v.keys():
if isinstance(dict_v[v], Foo):
# the process only happens for the objects of a specific class
dict_v[v].set_name(v)
#proof
print(a.get_name())
print(b.get_name())
print(c.get_name())

Related

Execute class function stored in variable on demand

I have two classes with functions:
from functools import partial
class A:
def __init__(self, collection):
self.collection = collection
def filter(self, val):
for element in self.collection:
if element.var == val:
return element
class B:
def __init__(self, var):
self.var = var
def test(self):
print('Element with variable ', self.var)
Now I want to have a class that can call a function on an object, fetched on the fly by another function, both stored in a variable and all executed when a certain function was called:
class C:
def __init__(self, fetch, function):
self.fetch = fetch
self.function = function
def run(self):
global base
# -----
# This is the code I need
base.fetch().function()
# ... and currently it's completely wrong
# -----
c = C(partial(A.filter, 5), B.test)
base = A([B(3), B(5), B(8)])
c.run()
Should print: Element with variable 5
You should pass base into run instead of messing with global. base doesn't have a fetch method, so you have to call the fetch function you have as an attribute with base as an argument. You can then send the return value of that call to function.
You're also applying partial to A.filter slightly wrong. Positional arguments are applied in order, so partial(A.filter, 5) will try to bind 5 to self, which will throw everything off. Instead, we need to give it the name of the parameter we wish to bind 5 to.
class C:
def __init__(self, fetch, function):
self.fetch = fetch
self.function = function
def run(self, a):
return self.function(self.fetch(a))
c = C(partial(A.filter, val=5), B.test)
c.run(A([B(3), B(5), B(8)]))
# Element with variable 5

Python: Why does class variable get assigned?

Here is my code:
class MyClass:
def __init__(self):
self.value = 0
def set_value(self, value):
self.value = 5
def get_value(self):
return self.value
value = print("Hello")
a = MyClass()
The output is:
Hello
What I do not understand is why print("Hello") gets executed. When I create an instance of the class only the instance variable is set to 0. Why self.value = 0 calls value = print("Hello")?
Can someone explain me this behavior?
The code evaluates the class when you execute it, and calls the print to define the class variable value.
The below example shows that it's printed before the instanciation.
class MyClass:
def __init__(self):
self.value = 0
def set_value(self, value):
self.value = 5
def get_value(self):
return self.value
value = print("Hello")
print('hi')
a = MyClass()
#output
>>> Hello
>>>hi
It doesn't. That print is executed because it's at the class level itself; the body of a class is executed at the time the class is defined. You would get that output even if you never instantiated MyClass.
Don't let the indentation trick you. value is not an instance variable. value is a class variable because it is defined in the class's scope. It's the same as doing:
class MyClass:
value = print("Hello")
....
Which means that the call to print will run at class definition time. In other words, when Python defines MyClass it also defines all of the class level variables - including value. To define value, it then calls print, which is why Hello is printed before you create an instance of MyClass.
If you want only instances of MyClass to print Hello, put the variable definition inside of the class constructor.
Side note: The print function returns None, so it's seems a bit strange that your assigning the value to a variable. Perhaps you were looking for something like input instead?
It does not, drop a = MyClass() and it will print "Hello" anyway. It executes code in the body when a class is defined:
class MyClass:
print(2 * 2)
# prints 4

Initialization of static/class lists/dictionaries in inner class(enum) in Python 3.5

Is it possible to initialize a static/class dictionary in an inner enum class like other variables?
# file Outer.py
from enum import Enum
class Outer:
def callInner(self):
all_a = Outer.Inner.ALL
print(all_a) # prints Inner.ALL instead of the list
all_b = Outer.Inner.ALL[:] # TypeError Inner is not subscriptable
for e in all_a: #Inner is not iterable
print(e.to_string())
class Inner(Enum):
A = 1
B = 2
ALL = [A,B]
NAMES = {A : "some_name_other_than_enum_a_name",
B : "some_name_other_than_enum_b_name"}
def to_string(self):
return Outer.Inner.NAMES[self.value]
if __name__ == '__main__':
o = Outer()
o.callInner()
The class Outer is the module with all the logic. The class Inner is an enum which does contain the enum key-value pairs A=1and B=2 as well as a list of all possible enums (or any interesting subset thereof). The idea is to have a list for quick reference and iteration/enumeration over those while to_string could be an arbritray method containing any logic. The name lookup is just a simplification to make the problem clear.
The issue here is not that you have an inner class, but that the inner class is an Enum; however, it is possible to have non-member attributes as part of an Enum class -- see this question and answer for the details.
To summarize, you need to make ALL and NAMES with some kind of descriptor to avoid having them transformed into enum members:
# inherit from `object` if using Python 2
class classattribute: # was called Constant in the linked answer
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __set__(self, _, value):
self.value = value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
and then in your Inner:
ALL = classattribute([A, B])
NAMES = classattribute({
A : "some_name_other_than_enum_a_name",
B : "some_name_other_than_enum_b_name",
})
This will avoid the errors you are getting in your callInner() method, but will add a new one at the print(e.to_string()) line:
AttributeError: 'int' object has no attribute 'to_string'
The reason for this is that constructing an Enum is a two-part process:
gather all the definitions:
{
'A':1,
'B':2,
'ALL':classattribute([A, B]),
'NAMES':classattribute({'A':..., 'B':...}),
'to_string':method(...),
}
transform anything not a __dunder__, _sunder_, nor descriptor into an enum member:
A -> <Inner.A: 1>
B -> <Inner.B: 2>
What this means is that when ALL and NAMES were being created, A and B were still ints, and ints don't have a to_string method. The easy way around that is to retrieve the enum member before trying to access those methods: self.Inner(e).to_string().
To pull it all together, here is what your code should look like:
# file Outer.py
from enum import Enum
class classattribute:
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)
class Outer:
def callInner(self):
all_a = Outer.Inner.ALL
print(all_a)
all_b = Outer.Inner.ALL[:]
for e in all_a: #Inner is not iterable
print(self.Inner(e).to_string())
class Inner(Enum):
A = 1
B = 2
ALL = classattribute([A,B])
NAMES = classattribute(
{A : "some_name_other_than_enum_a_name",
B : "some_name_other_than_enum_b_name"}
)
def to_string(self):
return Outer.Inner.NAMES[self.value]
if __name__ == '__main__':
o = Outer()
o.callInner()
and when run, this is what you get:
[1, 2]
some_name_other_than_enum_a_name
some_name_other_than_enum_b_name

Can't call static method inside class

I am trying to call a static method inside a class to populate the class variable.
import sys
import os
from HelpingData import *
class Inventory(object):
shipping_cost = 400.0
total_stock = calculate_total_stock.__func__()
def __init__(self, attributes={}):
self.inventory = {}
if attributes is None:
self.inventory = {}
else:
for key in attributes:
self.inventory[key] = attributes[key]
def getValue(self,attribute):
return self.inventory[attribute]
def setValue(self,attribute,value):
self.inventory[attribute]=value
#staticmethod
def calculate_total_stock():
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
And this is the error I am getting:
total_stock = calculate_total_stock.__func__()
NameError: name'calculate_total_stock' is not defined
What am I missing here?
You really don't need any workaround here, just give the calling method an additional level of direction.
In the example below you can call the PrintThis() method both internal and external to its defining class.
External:
Call as you normally would
MyClass.PrintThis('42')
Internal:
You must add self or the containing class
MyClass.PrintThis('42')
self.PrintThis('42')
To produce the error:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
The Fix:
class MyClass:
def __init__(self):
self.MyValue = 0
def IncrementValue(self):
self.MyValue += 1
self.PrintThis(f'From MyClass {self.MyValue}')
#staticmethod
def PrintThis(arg):
print(f'My Value: {arg}')
Run It
class Run:
def __init__(self):
mc = MyClass()
MyClass.PrintThis('From Outside')
mc.IncrementValue()
mc.IncrementValue()
My Value: From Outside
My Value: From MyClass 1
My Value: From MyClass 2
Why?
I'm not sure :-)
The only thing I noticed is that the static method (PrintThis) is a function, while the non-static method is a bound method.
I am sure there is some explanation to this behavior in Pythons documentation. Please share if you look it up :-)
I know this question is a few years old at this point, however it was the first hit when I googled the fault.
The code at the top level of the Inventory definition (i.e. class attributes and method definitions) runs before the name Inventory exists, so you can't call its own methods within the definition. As you have a #staticmethod, which doesn't require any class or instance argument, why not move it outside?
def calculate_total_stock(product_names, product_stock):
total_stock = dict((item, 0) for item in product_names)
for nation in product_stock:
for item in nation:
total_stock[item] += nation[item]
return total_stock
class Inventory(object):
SHIPPING_COST = 400.0
TOTAL_STOCK = calculate_total_stock(product_names, product_stock)
def __init__(self, attributes=None):
self.inventory = {}
if attributes is not None:
for key in attributes:
self.inventory[key] = attributes[key]
def get_value(self, attribute):
return self.inventory[attribute]
def set_value(self, attribute, value):
self.inventory[attribute] = value
Note that I have done some tidying up, particularly in terms of style and making the explicit arguments to calculate_total_stock.

create python factory method

I have the following simple example:
class CatZoo(object):
def __init__(self):
raise NotImplemented
#classmethod
def make_zoo_cat(cls, name, age, gender, location):
cls._names = name
cls._ages = age
cls._genders = gender
cls._location = location
return cls
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
cls._names = names
cls._ages = ages
cls._genders = genders
cls._location = location
return cls
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
And I am trying to create an object of this class by using the following:
cat_zoo = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print "The name is {}".format(cat_zoo.names)
This is just an example, I am just trying to make my factory methods work (make_zoo_cat, make_zoo_cats). The first will be passed one name, age, gender and location where the second would be passed a list of names, ages and genders and one location. If I run this code, I get the following output:
The name is <property object at 0x7fe313b02838>
Thanks,
Remove the NotImplemented initializer and actually create instances of your class, instead of mutating the class itself:
class CatZoo(object):
def __init__(self, name, age, gender, location):
self._names = name
self._ages = age
self._genders = gender
self._location = location
#classmethod
def make_zoo_cat(cls, name, ages, genders, location):
return cls.mak_zoo_cats([name], age, gender, location)
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
return CatZoo(names, age, gender, location)
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
Note that there was no real difference other than the method name between make_zoo_cat and make_zoo_cats, the difference in argument names doesn't change the functionality here.
Instead, I presumed that ._names should always be a list and that make_zoo_cat (singular) should create a CatZoo with one cat name in it.
Just remember that Python is not Java; you really don't need all those property objects, not where you could just access the attribute directly.
You didn't create any object in your code.
In your make_zoo_cats you return cls, so you still have a class not an instance of this class.
This code will print the yes
if CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC') == CatZoo:
print 'yes'
You agree than you can't do that, since name its a property it will only exist if you have an instance of that class.
CatZoo.names
to be able to use the property you need on instance of that class
something like that (this will raise in your code):
cat = CatZoo()
cat.names # I can do this now
An other point in your make_zoo_cat you create Class variables, those variables are accessible from the class (no need to have an instance on that class) but are "common" to all.
c1 = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print c1._names
print c1._ages
print c1._genders
print c1._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
c2 = CatZoo.make_zoo_cat('other', 42, 'F', 'FR')
print c2._names
print c2._ages
print c2._genders
print c2._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
print c1._names
print c1._ages
print c1._genders
print c1._location
the result will be someting like that:
squeakers
12
M
KC
**********
squeakers
12
M
KC
**********
other
42
F
FR
**********
other
42
F
FR
**********
other
42
F
FR
The first two give me the same result, and the last three as well, this is because they are class variables and you always have the same class so modifying one of those variable will affect the other

Categories

Resources