Can't call static method inside class - python

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.

Related

Prevent user of changing variable type in python

I have main class with a lot of attributes that are initially defined as an object of a Prop class. This Prop class has two attributes: its value and the options, which is a list of acceptable values for the attribute.
class Prop():
def __init__(self, value, *options):
self.value = value
self.options = options
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
The first important thing here is that _propx has to be an instance variable, since I will create more than one instance of Main.
The values of a Prop instance can either be a string or an integer, but the problem with this code is that I have to be sure that the user will do something like main._prop1.value = 1 and not main._prop1 = 1 otherwise it would break my code when doing _prop1.options. I don't want to use traits, thus I decided to make each _propx instance a kind of property, but I'm talking about a lot of instances and I don't want to define each setter especially because they will be all the same.
I found two solutions to solve this problem, the first is by using the same setter to all properties:
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
def set_prop(attr):
def set_value(self, value):
self.__dict__[attr].value = value
return set_value
prop1 = property(fset=set_prop('_prop1'))
prop2 = property(fset=set_prop('_prop2'))
The second is by using an auxiliary class and redefine its __set__:
class Aux():
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' + name
def __set__(self, obj, value):
print(self, obj, value, self.private_name)
obj.__dict__[self.private_name].value = value
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
prop1 = Aux()
prop2 = Aux()
the first on seems cleaner, but I have to pass the private name of each variable and I have to write the setter in the Main which I don't like because I would it to be as clean as possible. By other hand, in the second I have to use an auxiliary class.
My question is: is there a way of defining the setter in the Prop class? The reason why I couldn't find a way of doing this is that the Aux.__set__ seems to work only when I create an Aux instance as a class variable (static variable). This is also why I have to create a private and a public variable for each property. Is there a way of using __set__ to an instance (non-static) variable?

Python Variables across Class functions - how to call them?

Instead of using a global variable, I'm trying to make an instance of a variable in a class, as it seems to be best practice. How do I reference this variable across other functions in the class? I would have thought that Test.running_sum would work or at least running_sum in test_function, but I'm not having any luck with either. Thanks very much!
class Test:
def __init__(self):
self.root = None
running_sum = 0
def test_function(self):
print(Test.running_sum)
return
x = Test()
x.test_function()
Error:
Traceback (most recent call last):
File "so.py", line 1, in <module>
class Test:
File "so.py", line 10, in Test
x = Test()
NameError: name 'Test' is not defined
Use self parameter provided in the method signature.
Note that what you wrote is not a method, but an external function using class Test. To write a method of Test, the def should be at one level of indentation inside class Test as following:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
There are several things to add if you want an explanation behind this "best practice".
Assuming you write the following code:
class Test:
numbers = []
def add(self, value):
self.numbers.append(value)
The Test.numbers list is instantiated once and shared accross all instances of Test. Therefore, if 2 different instances add to the list, both act on the same list:
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a.numbers == b.numbers == Test.numbers
When creating instance variables in the __init__ function, __init__ will be run at each instantiation, and therefore, the list will no longer be shared because they will be created for each individual instances.
class Test:
def __init__(self):
self.numbers = []
def add(self, number):
self.numbers.append(number)
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a != b
As an object attribute: each object gets its own.
Test is the class; self is the Test object that invoked the method.
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
self.running_sum += 1
print(self.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
1
As a class attribute: all objects share the same variable.
self.__class__ is the class of the invoking object (i.e. Test).
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
self.__class__.running_sum += 1
print(self.__class__.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
2
how do I reference this variable across other functions in the class
Several things I see wrong here. First of all, you are calling running_sum on the class itself which doesn't make sense since you are declaring running_sum as an attribute of an instance of Test. Second, from the way you formatted your question, it seems that test_function is outside of the class Test which doesn't make sense since you are passing self to it, implying it is an instance method. To resolve you could do this:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
Then again this also is weird... Why would you need a "test_function" when you can simply test the value of running_sum by simply doing:
x = Test()
x.running_sum
In your __init__ function, you've created a local variable. That variable will no longer exist after the function has completed.
If you want to create a variable specific to the object x then you should create a self.running_sum variable
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
print(self.running_sum)
If you want to create a variable specific to the class Test then you should create a Test.running_sum variable.
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
print(Test.running_sum)

What is the order of namespaces in inheritance?

A derived class has access to its base class member functions implicitly, unless I am mistaken. A derived class can also access its base class' attributes by prefixing a call to them like this: BaseClass.base_attribute. But I seemingly do not understand how instances of a derived class can use the methods of the base class. Example:
class Visitor():
""" Interface to Visitor
provide an interface to visitors that
perform an operation on a data collection """
def visitProduce():
pass
def visitMeat():
pass
def visitBakedGoods():
pass
def visitDairy():
pass
def visitNonFood():
pass
class PriceVisitor(Visitor):
__cost = 0.0 # total cost of groceries
def __init__(self):
self.__cost = 0.0
def visitProduce(self, p):
self.__cost += p.price()
def visitMeat(self, m):
self.__cost += m.price()
def visitBakedGoods(self, b):
self.__cost += b.price()
def visitDairy(self, d):
self.__cost += d.price()
def visitNonFood(self, nf):
self.__cost += nf.price()
class Groceries():
shopping_cart = [] # list of grocery items
def Groceries(self):
self.shopping_cart = []
def addProduce(self, p):
pass
def addMeat(self, m, lb):
pass
def addBakedGoods(self, b):
pass
def addDairy(self, d):
pass
def addNonFood(self, nf):
pass
def accept(self, v):
pass
def getShoppingCart(self):
print(self.shopping_cart)
def calculateCost(self, v):
for item in self.shopping_cart:
item.accept(v)
item.details()
print('Total cost is: $', v.__cost)
class Produce(Groceries):
def addProduce(self):
Groceries.shopping_cart.append(self)
def accept(self, v):
v.visitProduce(self)
def price(self):
return self.__price
def details(self):
print(self.__name, ' for: $', self.__price + '')
class Apples(Produce):
__name = None
__price = 3.25
def __init__(self, name):
self.__name = name
And here is a test of the Apple, Produce, Groceries, and PriceVisitor classes
import VisitorPattern as vp
def main():
# Visitor object
my_visitor = vp.PriceVisitor()
# Grocery object stores objects in its shopping_cart attribute
my_groceries = vp.Groceries()
# Add items
red_apple = vp.Apples('red apple')
gold_apple = vp.Apples('gold apple')
red_apple.addProduce()
gold_apple.addProduce()
my_groceries.getShoppingCart()
my_groceries.calculateCost(my_visitor)
if __name__ == '__main__':
main()
Now, the way I understand it is that upon the construction of the instance of Apple, it has access to Produce's method price(). Calling this method with an instance of the Apple class will then pass its own instance in place of the 'self'. The program then returns the value of the __price attribute belonging to the instance calling the method, in this case Apple. However, I get this error:
C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4>python test.
py
[<VisitorPattern.Apples object at 0x026E0830>, <VisitorPattern.Apples object at
0x026E0910>]
Traceback (most recent call last):
File "test.py", line 23, in <module>
main()
File "test.py", line 20, in main
my_groceries.calculateCost(my_visitor)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 60, in calculateCost
item.accept(v)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 71, in accept
v.visitProduce(self)
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 28, in visitProduce
self.__cost += p.price()
File "C:\Users\josep_000\Documents\School\Summer 2015\Python Assignment 4\Visi
torPattern.py", line 74, in price
return self.__price
AttributeError: 'Apples' object has no attribute '_Produce__price'
How does the binding and namespaces actually work in inheritance? I could just write the price() method in each of Produce's derived classes, but that would defeat the point of inheritance. I think my problem also stems from name mangling, but still don't know what happens if I don't make my attributes 'private'. Clarification would be great. Thanks
Edit
I declared the constructor of Groceries wrong:
# Wrong way
def Groceries(self):
self.shopping_cart = []
# Should be
def __init__(self):
self.__shopping_cart = []
The product of a full time job and homework in the evening
What is the order of namespaces in inheritance?
Python uses the Method Resolution Order to find the method bound to that instance of the object.
It also invokes name mangling, which is why you can't find the method, _Produce__price. You're trying to use .__price but when it is inherited, Python adds the name of the class to the front of the name. Don't use two underscores, change the two underscores to one, and your code will work as you expect, and you'll consistently look up ._price which won't invoke the name mangling.
See the docs for more on this:
https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
Not really a direct answer to all your questions but I hope the following code sheds some light on how to do inheritance in Python.
class Produce(object):
def __init__(self, name=None, price=None):
self.__name = name
self.__price = price
def __str__(self):
return self.__name
#property
def bulk_price(self):
return self.__price * 100
class Apple(Produce):
def __init__(self, name="Apple"):
self.__name = name
self.__price = 3.25
super(self.__class__, self).__init__(self.__name, self.__price)
a = Apple("Gold Apple")
print a
print a.bulk_price
# Gold Apple
# 325.0
As you can see, I made name and price inaccessible in both classes. This way, I cannot just call them explicitly, i.e. a.__price. By using super as well in the child class, I am able to avoid referring to the base class further while still having access to its methods.
I have saw your error, your parent need to call child's function, but you have not transferred child to parent, so it will get the errors.Now I give my example:
class A:
def __init__(self, handler):
self.a = 5
self.real_handler = handler
def get(self):
print "value a = %d"%self.a
self.real_handler.put()
class B(A):
def __init__(self):
A.__init__(self, self) ##transport B to A
self.b = 3
def get(self):
print "value b is %d"%self.b
A.get(self)
def put(self):
self.b = 6
print "value b change into %d"%self.b
if __name__=="__main__":
b = B()
b.get()
In parent B, it will call the child A's fuction put(). I hope this can help you.

Python AttributeError: property cannot overwrite inherited attribute?

I still don't fully understand when and how to use properties. Here I have a class SpecialCar which is inheriting Car. The variable summer_tire should basically be equivalent to tire, except for the name. So whenever I am asking for either of those two, I want to get summer_tire.
Using #property results in an error. Deleting the #property line will print 0, but I want to get 2.
class Car():
def __init__(self):
self.tire = 0
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
i = SpecialCar()
print(i.tire)
You declared a property that doesn't have a setter, thus self.tire = 0 in Car.__init__ fails.
You could give your new property a setter:
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
#tire.setter
def tire(self, new_tire):
self.summer_tire = new_tire
or you could avoid calling Car.__init__ altogether, or make Car.tire a class attribute, set as part of the class and replaced with the property in subclasses.

Can I iterate over a class in Python?

I have a class that keeps track of its instances in a class variable, something like this:
class Foo:
by_id = {}
def __init__(self, id):
self.id = id
self.by_id[id] = self
What I'd like to be able to do is iterate over the existing instances of the class. I can do this with:
for foo in Foo.by_id.values():
foo.do_something()
but it would look neater like this:
for foo in Foo:
foo.do_something()
is this possible? I tried defining a classmethod __iter__, but that didn't work.
If you want to iterate over the class, you have to define a metaclass which supports iteration.
x.py:
class it(type):
def __iter__(self):
# Wanna iterate over a class? Then ask that class for iterator.
return self.classiter()
class Foo:
__metaclass__ = it # We need that meta class...
by_id = {} # Store the stuff here...
def __init__(self, id): # new isntance of class
self.id = id # do we need that?
self.by_id[id] = self # register istance
#classmethod
def classiter(cls): # iterate over class by giving all instances which have been instantiated
return iter(cls.by_id.values())
if __name__ == '__main__':
a = Foo(123)
print list(Foo)
del a
print list(Foo)
As you can see in the end, deleting an instance will not have any effect on the object itself, because it stays in the by_id dict. You can cope with that using weakrefs when you
import weakref
and then do
by_id = weakref.WeakValueDictionary()
. This way the values will only kept as long as there is a "strong" reference keeping it, such as a in this case. After del a, there are only weak references pointing to the object, so they can be gc'ed.
Due to the warning concerning WeakValueDictionary()s, I suggest to use the following:
[...]
self.by_id[id] = weakref.ref(self)
[...]
#classmethod
def classiter(cls):
# return all class instances which are still alive according to their weakref pointing to them
return (i for i in (i() for i in cls.by_id.values()) if i is not None)
Looks a bit complicated, but makes sure that you get the objects and not a weakref object.
Magic methods are always looked up on the class, so adding __iter__ to the class won't make it iterable. However the class is an instance of its metaclass, so the metaclass is the correct place to define the __iter__ method.
class FooMeta(type):
def __iter__(self):
return self.by_id.iteritems()
class Foo:
__metaclass__ = FooMeta
...
Try this:
You can create a list with a global scope, define a list in the main module as follows:
fooList = []
Then add:
class Foo:
def __init__(self):
fooList.append(self)
to the init of the foo class
Then everytime you create an instance of the Foo class it will be added to the fooList list.
Now all you have to do is iterate through the array of objects like this
for f in fooList:
f.doSomething()
You can create a comprehension list and then call member methods as follows:
class PeopleManager:
def __init__(self):
self.People = []
def Add(self, person):
self.People.append(person)
class Person:
def __init__(self,name,age):
self.Name = name
self.Age = age
m = PeopleManager()
[[t.Name,t.Age] for t in m.People]
call to fill the object list:
m = PeopleManager()
m.Add( Person("Andy",38))
m.Add( Person("Brian",76))
You can create a class list and then call append in the init method as follows:
class Planet:
planets_list = []
def __init__(self, name):
self.name = name
self.planets_list.append(self)
Usage:
p1 = Planet("earth")
p2 = Planet("uranus")
for i in Planet.planets_list:
print(i.name)

Categories

Resources