Python - How explicitly change evaluation of class object? - python

The function my_func gets one argument, that could be integer or a class:
class SomeClass:
val = 0
def __init__(self):
self.val = 0
def set(self, val):
self.val = val
def get(self):
return self.val
and I want to work with both types as one.
Can I somehow explicitly tell Python, that evaluation of an object of type SomeClass, should return the val field of the object, but not the object it selves.
That means to get the following code work (somehow):
s = SomeClass()
s.set(3)
test = s # this line should assign value 3(int) to test
test # prints 3

Not really, no, not in the broad sense that you seem to ask for. You can overload many operations, not assignment. That's because variables are just references to objects and have no type attached. At any given time there can be many references to your object. It matters how you use the object, not what you assigned it to.
Instead, I'd make your class convertible to an integer. You can do so with the object.__int__ hook; your setter would explicitly call int() on the object passed in:
class SomeClass:
val = 0
def __init__(self):
self.val = 0
def set(self, val):
self.val = int(val)
def get(self):
return self.val
def __int__(self):
return self.val
If you always use these objects in integer arithmetic, you could add hooks for those operations, such as object.__add__ and object.__sub__; again, it is the context in which the object is used that matters.

Related

Can class instances be accessed via an index in python?

Consider for example that we have a class 'Agent' as below:
class Agent:
def __init__(self, number):
self.position = []
self.number = number
for i in range(number):
self.position.append([0, 0])
I can make an instance of the class by:
agent = Agent(10)
and then access the i'th agent's position by:
agent.position[i]
However, this does not seem elegant enough and to me it's a bit counter-intuitive. Instead I want to index the class instance itself. For example:
pos_i = agent[i].position
which should return the same answer as the one-line code above. Is there a way to accomplish this?
If you want to do that, you just need a class-level container, with all instances.
Since your positions, given your example, are created in an arbitrary order, I'd suggest using a dictionary.
You can just fill the class-level "position" dictionary. You could then just implement the __getitem__ method to retrieve elements from this dictionary:
class Agent:
position = {}
def __new__(cls, pos):
if pos in cls.position:
return cls.position[pos]
instance = super().__new__(cls)
cls.position[pos] = instance
return instance
def __getitem__(self, item):
return self.position[pos]
This, however, will only allow you to retrieve an instance given the position from an instance - i.e.:
agent_5 = Agent(5)
agent_10 = agent_5[10]
would work, but not:
agent_10 = Agent[10]
If you want that, you have to use a custom metaclass, and put the __getitem__ method there:
class MAgent(type):
def __getitem__(cls, item):
return cls.position[pos]
class Agent(metaclass=MAgent):
position = {}
def __new__(cls, pos):
if pos in cls.position:
return cls.position[pos]
instance = super().__new__(cls)
cls.position[pos] = instance
return instance
If you want to overload the indexing operator just overload the __getitem__ method in the class.
class Agent:
def __getitem__(self, key):
return self.position[key]
>>> myobj = MyClass()
>>> myobj[3]

How can I keep track of object instances in a list with python without using constructors (__init__)?

I want to keep track of object instances of a given class/subclasses in Python given my particular framework I cannot use constructors. As an alternative I could use either decorators or Singletons objects.
Are there any other alternatives except using global variables ?
You could use a descriptor. That is, it's an object that can and will keep track of what things are where with the caveat that it will only do so if you've assigned a value to do it. Here's an example:
class RevealAccess(object):
"""
A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print 'Retrieving', self.name
return self.val
def __set__(self, obj, val):
print 'Updating', self.name
self.val = val
This is taken out from the documentation. However, add the following from this SO Q&A:
_instances = []
def __new__(cls, *args, **kw):
instance = object.__new__(cls)
cls._instances.append(instance)
return instance
#classmethod
def get_instances(cls):
return self._instances
and you've achieved your goals:
class Foo(object):
desc = MyDesc('yo')
foo = Foo()
Foo.get_instances().length

Python class and variables

Learning Python and I ran into some problems when I was working on making a linked list class.
This is just a quick node and dirty node class. In java I would of down private Node next and private int val but I only knew of global as the python cousin. How does this look?
#class Node class
class Node(object):
global next
global val
def __init__(self):
next
val
def setNext(self, aNext):
self.next = aNext
def getNext(self):
return self.next
def setVal(self, aVal):
self.val = aVal
def getVal(self):
return self.val
Then I tried to use a Node in another class with
from Node import *
head = Node()
How ever I am getting an error of undefined variable. Sorry for the simple question just new to python. Appreciate the help.
I would implement this this way:
class Node(object):
def __init__(self, next=None, val=None):
self.next = next
self.val = val
That's it. No getters or setters - Python doesn't use them. Instead, you refactor into a property if you need to move away from the basic attribute reference logic.
You can then create nodes with or without values or successors:
tailnode = Node()
tailnode.val = 'foo'
midnode = Node(val='bar')
midnode.next = tailnode
headnode = Node(val='baz', next=midnode)
You don't need the "global val" / "global next" .. It's a mistake even.
instead just write
val = None
next = None
and initiate them in the __init__()
Meaning, the first lines in your class should be like:
class Node(object):
# You can choose whether to initialize the variables in the c'tor or using your setter methods
def __init__(self, val=None, next=None):
self.next = next
self.val = val
If you really want private variables in Python… then you don't want private variables, and should read Peter DeGlopper's answer.
If you still really, really want private variables in Python… well, you can't have them. But you can have "cooperatively private" variables—variables that nobody will find unless they go looking for them, and that won't clutter the screen when you introspect things in the interpreter, and so on, and, most importantly, that Python programmers know, by convention, that they aren't supposed to touch. All you have to do is start the name with an underscore.
However, your code isn't creating member variables at all, for a number of reasons.
First, global does not declare or define a variable; all it does is tell Python, "when you see this variable later, don't use the normal rules to figure out if it's local or global, always use the global copy". You still have to assign a value to the variable somewhere; otherwise, you'll get a NameError.
Next, variables that you assign in the class definition are class members—similar to Java's static members, although not identical. Each class member is shared by all instances of the class. That's not what you want here; each Node is supposed to have its own separate val and next, not share one with all other Nodes, right?
Normal instance member variables are always accessed through dot syntax—as self.foo from inside the class's methods, or as spam.foo from outside.
So, where do you declare those? You don't. Python doesn't declare anything. You can add new members to an object at any time. The usual way to create a standard set of instance members is in the __init__ method:
class Node(object):
def __init__(self):
self._next = None
self._val = None
def setNext(self, aNext):
self._next = aNext
def getNext(self):
return self._next
def setVal(self, aVal):
self._val = aVal
def getVal(self):
return self._val
But really, you can just let the setters create them. That way, you'll catch the error if someone calls getNext without having called setNext first (which is, I assume, illegal).
class Node(object):
def setNext(self, aNext):
self._next = aNext
def getNext(self):
return self._next
def setVal(self, aVal):
self._val = aVal
def getVal(self):
return self._val
Or, alternatively, force the user to initialize the object with valid values at construction time:
def __init__(self, next, val):
self._next = next
self._val = val
Again, there's no good reason to use setters and getters in the first place in Python.
So, the simplest implementation of your class is:
class Node(object):
pass
While the most Pythonic is:
class Node(object):
def __init__(self, next, val):
self.next = next
self.val = val
… which, you'll notice, is Peter DeGlopper's answer, which, as I said at the start, is probably what you want. :)
Python doesn't really use private variables.
Something like this would be best:
class Node(object):
def __init__(self):
self.val = None
self.next = None
Then, you make and set the node like this:
>>> node = Node()
>>> node.val = 5
>>> node2 = Node()
>>> node2 = 1
>>> node.next = node2
>>> node.next.val
1
If you want to create node with Node(5, Node(1)), use:
class Node(object):
def __init__(self, value=None, next=None):
self.value = value
self.next = next

dynamically adding callable to class as instance "method"

I implemented a metaclass that tears down the class attributes for classes created with it and builds methods from the data from those arguments, then attaches those dynamically created methods directly to the class object (the class in question allows for easy definition of web form objects for use in a web testing framework). It has been working just fine, but now I have a need to add a more complex type of method, which, to try to keep things clean, I implemented as a callable class. Unfortunately, when I try to call the callable class on an instance, it is treated as a class attribute instead of an instance method, and when called, only receives its own self. I can see why this happens, but I was hoping someone might have a better solution than the ones I've come up with. Simplified illustration of the problem:
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
# This doesn't work as I'd wish
def __call__(self, instance):
return self.name + str(self.val + instance.val)
def get_methods(name, foo_val):
foo = Foo(name, foo_val)
def bar(self):
return name + str(self.val + 2)
bar.__name__ = name + '_bar'
return foo, bar
class Baz(object):
def __init__(self, val):
self.val = val
for method in get_methods('biff', 1):
setattr(Baz, method.__name__, method)
baz = Baz(10)
# baz.val == 10
# baz.biff_foo() == 'biff11'
# baz.biff_bar() == 'biff12'
I've thought of:
Using a descriptor, but that seems way more complex than is necessary here
Using a closure inside of a factory for foo, but nested closures are ugly and messy replacements for objects most of the time, imo
Wrapping the Foo instance in a method that passes its self down to the Foo instance as instance, basically a decorator, that is what I actually add to Baz, but that seems superfluous and basically just a more complicated way of doing the same thing as (2)
Is there a better way then any of these to try to accomplish what I want, or should I just bite the bullet and use some closure factory type pattern?
One way to do this is to attach the callable objects to the class as unbound methods. The method constructor will work with arbitrary callables (i.e. instances of classes with a __call__() method)—not just functions.
from types import MethodType
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
def __call__(self, instance):
return self.name + str(self.val + instance.val)
class Baz(object):
def __init__(self, val):
self.val = val
Baz.biff = MethodType(Foo("biff", 42), None, Baz)
b = Baz(13)
print b.biff()
>>> biff55
In Python 3, there's no such thing as an unbound instance method (classes just have regular functions attached) so you might instead make your Foo class a descriptor that returns a bound instance method by giving it a __get__() method. (Actually, that approach will work in Python 2.x as well, but the above will perform a little better.)
from types import MethodType
class Foo(object):
def __init__(self, name, val):
self.name = name
self.val = val
self.__name__ = name + '_foo'
self.name = name
def __call__(self, instance):
return self.name + str(self.val + instance.val)
def __get__(self, instance, owner):
return MethodType(self, instance) if instance else self
# Python 2: MethodType(self, instance, owner)
class Baz(object):
def __init__(self, val):
self.val = val
Baz.biff = Foo("biff", 42)
b = Baz(13)
print b.biff()
>>> biff55
The trouble you're running into is that your object is not being bound as a method of the Baz class you're putting it in. This is because it is not a descriptor, which regular functions are!
You can fix this by adding a simple __get__ method to your Foo class that makes it into a method when it's accessed as a descriptor:
import types
class Foo(object):
# your other stuff here
def __get__(self, obj, objtype=None):
if obj is None:
return self # unbound
else:
return types.MethodType(self, obj) # bound to obj

Confusion with properties

I am new to using properties, so I put together a simple test as shown below. In my tests, I made two classes "Test1" and "Test2" where each is meant to hold one value. I am attempting to use a property to govern access to the pseudo-hidden "val" attribute. This current test does not restrict any inputs or outputs of the "val" attribute as this program was only meant as a proof of concept. The two test classes shown below yield the same results and are supposed to represent the different methods to construct a property. The example uses for properties I am referring to are found on the python docs here.
As per the documentation:
If then c is an instance of C, c.x will invoke the getter, c.x = value will invoke the setter and del c.x the deleter.
where C is their test class. I thought that by setting the value the way I did would change _val and leave val as a property. However it seems to me that my method of accessing the properties setter is actually replacing the property with the integer 5 unless I am mistaken. I hope someone can clarify my confusion.
class Test1:
def __init__(self):
self._val = 0
def setVal(self,newVal):
self._val = newVal
val = property(lambda self: self._val, setVal, None, "Property for value")
def __str__(self):
return "Value: {}".format(self.val)
class Test2:
def __init__(self):
self._val = 0
#property
def val(self):
return self._val
#val.setter
def setVal(self,newVal):
self._val = newVal
def __str__(self):
return "Value: {}".format(self.val)
def verify(a):
print("\nCheck with {}".format(a.__class__.__name__))
print("Value check:",a.val)
a.val = 5
print("Value after a.val = 5 is:",a.val)
print("The actual value is:",a._val)
def main():
verify(Test1())
verify(Test2())
if __name__ == '__main__':
main()
From documentation:
property([fget[, fset[, fdel[, doc]]]])
Return a property attribute for new-style classes (classes that derive from object).
Descriptors are only invoked for new style objects or classes. Your using old-style classes. Inherits from a base type object:
class Test1(object):
# your code
class Test2(object):
def __init__(self):
self._val = 0
#property
def val(self):
return self._val
#val.setter
def val(self,newVal): # should be named as property
self._val = newVal
def __str__(self):
return "Value: {}".format(self.val)
This work fine:
>>> verify(Test1())
Check with Test1
('Value check:', 0)
('Value after a.val = 5 is:', 5)
('The actual value is:', 5)
Read more about difference between new-style classes and classic classes.

Categories