Proxy Class in Python 3 - python

I wrote a simple Proxy class in python3, but I have a problem with "was_called" function
class Proxy:
last_invoked = ""
calls = {}
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item):
attrs = dir(self._obj)
if item in attrs:
Proxy.last_invoked = item
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
if item in Proxy.calls.keys():
Proxy.calls[item] += 1
else:
Proxy.calls[item] = 1
return getattr(self._obj, item)
else:
raise Exception('No Such Method')
def last_invoked_method(self):
if Proxy.last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return Proxy.last_invoked
def count_of_calls(self, method_name):
if method_name in Proxy.calls.keys():
return Proxy.calls[method_name]
return 0
def was_called(self, method_name):
if method_name in Proxy.calls.keys():
if Proxy.calls[method_name] > 0: return True
return False
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Radio()
radio_proxy = Proxy(radio)
radio.number = 3
radio_proxy.number = 3
radio_proxy.power()
print(radio_proxy.was_called("number"))
print(radio_proxy.was_called("power"))
"was_called" function is work for functions and attributes that is in radio at first such as "power", but it's not work for new attributes that we add such as "number".
I expect for both print "True", because both of "power" and "number" is called. but first print return False!
What do you suggest?

def Proxy(class_type):
class ProxyClass(class_type):
def __init__(self, *args, **kwargs):
# Set your _calls and _last_invoked here, so that they are not class attributes (and are instead instance attributes).
self._calls = {}
self._last_invoked = ""
# Pass the arguments back to the class_type (in our case Radio) to initialize the class.
super().__init__(*args, **kwargs)
def __getattribute__(self, item):
# We must do this prelimary check before continuing on to the elif statement.
# This is since _calls and _last_invoked is grabbed when self._last_invoked/self._calls is called below.
if item in ("_calls", "_last_invoked"):
return super(ProxyClass, self).__getattribute__(item)
elif not item.startswith("_"):
self._last_invoked = item
self._calls[item] = 1 if item not in self._calls.keys() else self._calls[item] + 1
return super(ProxyClass, self).__getattribute__(item)
def __setattr__(self, item, val):
# Wait until _calls is initialized before trying to set anything.
# Only set items that do not start with _
if not item == "_calls" and not item.startswith("_"):
self._calls[item] = 0
super(ProxyClass, self).__setattr__(item, val)
def last_invoked_method(self):
if self._last_invoked == "":
raise Exception('No Method Is Invoked')
else:
return self._last_invoked
def count_of_calls(self, method_name):
return self._calls[method_name] if method_name in self._calls.keys() else 0
def was_called(self, method_name):
return True if method_name in self._calls.keys() and self._calls[method_name] > 0 else False
return ProxyClass
#Proxy
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
def get_channel(self):
return self._channel
def set_channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
radio = Proxy(Radio)()
radio.number = 3 # Notice that we are only setting the digit here.
radio.power()
print(radio._calls)
print(radio.number) # Notice that this when we are actually calling it.
print(radio._calls)
outputs:
{'is_on': 0, 'volume': 0, 'number': 0, 'power': 1}
3
{'is_on': 0, 'volume': 0, 'number': 1, 'power': 1}
A few modifications here and there, but you should be able to see the bigger idea by reading through the code. From here you should be able to modify the code to your liking. Also note that any variable that starts with _ is automatically removed from the _calls dictionary.
If you rather not use the decorator #Proxy, you may initialize your Radio class (as a proxy) like so:
# Second parentheses is where your Radio args go in.
# Since Radio does not take any args, we leave it empty.
radio_proxy = Proxy(Radio)()
Also, make sure to understand the difference between class attributes, and instance attributes.
Edit:
class Test:
def __init__(self, var):
self.var = var
self.dictionary = {}
def __getattribute__(self, item):
print("we are GETTING the following item:", item)
# If we don't do this, you end up in an infinite loop in which Python is
# trying to get the `dictionary` class to do `self.dictionary['dictionary'] = ...`
if item == "dictionary":
super(Test, self).__getattribute__(item)
else:
self.dictionary[item] = "Now we can use this!"
return super(Test, self).__getattribute__(item)
def __setattr__(self, item, key):
print("we are SETTING the following item:", item)
super(Test, self).__setattr__(item, key)
Notice:
test = Test(4)
outputs:
we are SETTING the following item: var
we are SETTING the following item: dictionary
then following it:
test.var
outputs:
we are GETTING the following item: var
we are GETTING the following item: dictionary

Related

next(iterable) works but 'for' loops just creates an infinite loop

I'm trying to recursively iterate through all my nodes, however I get an infinite loop.
class Dictionary:
# STARTS CLASS NODE ################################################
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.nextNode = None
def insert(self, key, value):
if self.nextNode is None or key < self.nextNode.key:
new = Dictionary.Node(key, value)
new.nextNode = self.nextNode
self.nextNode = new
elif key > self.nextNode.key:
self.nextNode.insert(key, value)
else:
pass
def __iter__(self):
if self.nextNode is None:
return self
else:
return self.__next__().__iter__()
def __next__(self):
return self.nextNode
def __str__(self):
return f'{self.key} : {self.value}'
# ENDS CLASS NODE ################################################
def __init__(self):
self.__head = None
def insert(self, key, value):
if self.__head is None or key < self.__head.key:
new = self.Node(key, value)
new.nextNode = self.__head
self.__head = new
elif key > self.__head.key:
self.__head.insert(key, value)
else:
pass
def __iter__(self):
return self.__head
def __str__(self):
return str(self.__head)
d = Dictionary()
d.insert(2, 2)
d.insert(3, 3)
d.insert(4, 4)
d.insert(5, 5)
When I do:
p = iter(d)
print(p)
print(next(p))
print(next(next(p))
it works, but when I do
for i in p:
print(i)
I get an infinite loop that prints 3s. It doesn't even jump nodes.
Isn't a for loop just the same thing as doing iter() and then using next() a bunch of times until I hit a condition (None)? Shouldn't I be getting the same thing for these? What am I doing wrong?
Your test code:
p = iter(d)
print(p)
print(next(p))
print(next(next(p)))
is not what happens in a for-loop.
That would be more like this:
it = iter(d)
print(next(it))
print(next(it))
...
and using your class, this would just give you the same element over and over again.
An iterator is an object that you call next on repeatedly and it gives you a sequence of elements, until it raises an exception to signal that it is finished. Your __next__ implementation just returns self.nextNode each time it is called. It won't ever raise an exception, so there's no signal that the iteration is finished.
If you want to write an iterator explicitly, you would need to create and update an object that keeps track of how far you had got through the iteration, which could be something like this:
class NodeIter:
def __init__(self, start):
self.cur = start
def __iter__(self):
return self
def __next__(self):
if self.cur is None:
raise StopIteration()
x = self.cur
self.cur = x.nextNode
return x
And then in your Dictionary you would have:
def __iter__(self):
return NodeIter(self.__head)
Alternatively, here is a quick way to write __iter__ that would go through all the nodes:
def __iter__(self):
cur = self.__head
while cur is not None:
yield cur
cur = cur.nextNode
This is a generator function, which is an easy way to generate a sequence using yield statements.

Good way to writing unit test in python

I have simply 'to do' app in python 2.7 and I wrote for this some unit test. This is my first time with python unit test and I just wanna to know idea of unit test.
Someone can tell me whether I'm going in the right direction?
How to improve this tests?
How to check message in IndexError is correct? for this ("IndexError('Note doesn\'t exist')" or IndexError('Returned more then one entry') )
App:
# coding: utf-8
from __future__ import unicode_literals
from shutil import copyfile
import json
import os
DATABASE = 'notes_data/notes.json'
BOARDS = ['to do', 'in progress', 'done']
class NotesManagerMixin(object):
def count(self):
return len(self.notes)
def filter(self, *args, **kwargs):
result = self.notes
for key, value in kwargs.iteritems():
result = [
note for note in result
if getattr(note, key, None) == value or
note.message.startswith(str(value)) or
note.message.endswith(str(value))
]
return NotesQueryset(result)
def get(self, *args, **kwargs):
notes = self.filter(*args,**kwargs)
if notes.count() == 0:
raise IndexError('Note doesn\'t exist')
elif notes.count() == 1:
return notes[0]
else:
raise IndexError('Returned more then one entry')
def first(self):
return self.notes[0]
def last(self):
return self.notes[-1]
class NotesQueryset(NotesManagerMixin):
def __init__(self, notes):
self.notes = [note for note in notes]
def update(self, *args, **kwargs):
for note in self.notes:
for key, value in kwargs.items():
setattr(note, key, value)
note.save()
return self
def delete(self):
for note in self.notes:
note.delete()
return self
def __getitem__(self, idx):
return self.notes[idx]
def __str__(self):
return str(self.notes)
def __repr__(self):
return self.__str__()
class NotesManager(NotesManagerMixin):
def __init__(self):
self.notes = []
def __iter__(self):
return self.next()
def __generate_id(self):
"""
Funkcja pomocnicza do pobrania pewnej wolnej wartoĊ›ci indexu.
"""
try:
return max(note.id for note in self.notes) + 1
except ValueError:
return 1
def all(self):
return NotesQueryset(self.notes)
def add(self, idx, board, message):
self.notes.append(Note(idx=idx, board=board, message=message))
def create(self, board, message):
note = Note(
idx=self.__generate_id(),
board=board,
message=message
)
note.clean()
self.notes.append(note)
note.save()
return note
def next(self):
for note in self.notes:
yield note
def to_dict(self):
return [note.to_dict() for note in self.notes]
class Note(object):
objects = NotesManager()
def __init__(self, idx, board, message):
self.id = idx
self.board = board
self.message = message
def __str__(self):
return 'ID: {}, Board: {}, Message: {}'.format(
self.id,
self.board,
self.message
)
def __repr__(self):
return self.__str__()
def clean(self):
if not self.message:
raise ValueError('Message is required')
if self.board not in BOARDS:
raise ValueError('Board "{}" doesn\'t exists'.format(self.board))
if type(self.id) != int:
raise ValueError('Note id "{}" is invalid'.format(self.id))
def save(self):
for key, note in enumerate(self.objects):
if note.id == self.id:
self.objects.notes[key] = self
break
with open(DATABASE, 'w') as database_file:
json.dump(self.objects.to_dict(), database_file, indent=4)
return True
def delete(self):
for key, note in enumerate(self.objects.notes):
if note.id == self.id:
self.objects.notes.pop(key)
with open(DATABASE, 'w') as database_file:
json.dump(self.objects.to_dict(), database_file, indent=4)
def to_dict(self):
return {
'id': self.id,
'message': self.message,
'board': self.board
}
def load_initial_data():
with open(DATABASE, 'r') as database_file:
json_data = json.load(database_file, encoding='utf-8')
for item in json_data:
Note.objects.add(
idx=item['id'],
board=item['board'],
message=item['message'],
)
load_initial_data()
unit tests:
import unittest
from notes_manager_v2 import NotesQueryset, Note, load_initial_data, NotesManagerMixin
class TestNotesQueryset(unittest.TestCase):
def test_filter(self):
actual = Note.objects.all().filter(board='in progress') # get all notes with board 'in progress'
expected = []
for note in Note.objects.all():
if note.board == 'in progress':
expected.append(note)
self.assertItemsEqual(actual, expected)
def test_get(self):
actual = [Note.objects.all().get(id=1)] # get note with method get
expected = []
for note in Note.objects.all(): # search note with index 1
if note.id == 1:
expected.append(note)
self.assertEqual(actual, expected)
def test_get_fail_1(self):
self.assertRaises(IndexError, lambda:Note.objects.all().get(id=9868976)) # thos note dont exist should raise IndexError
def test_update(self):
from_board = 'to do'
to_board = 'done'
before_change = Note.objects.filter(board=from_board) # use filter method to get all notes with board 'to do'
actual = Note.objects.filter(board=from_board).update(board=to_board) # update all board
self.assertNotEqual(before_change, actual) # check for difference
notes = Note.objects.all()
for note in actual:
self.assertIn(note, notes) # check notes are updated
def test_delete(self):
to_delete = Note.objects.filter(id=2).delete() # find note with filter method and delete it
notes = Note.objects.all()
self.assertNotIn(to_delete, notes)
def test_create(self):
new_note = Note.objects.create(message='lorem ipsum', board='in progress') # create new note
notes = Note.objects.all()
self.assertIn(new_note, notes) #
if __name__ == '__main__':
unittest.main()
Have you looked at the documentation? See doctest. They are an easy way to integrate unit tests into python code. Another option is the unittest framework.

Provide a list of methods in a class for use outside of it

How can I provide a list of methods in a class for use outside of it?
What I'm looking for
When I create an object from a class extending a higher class, I want to get a list of specific methods, some "meta data" of them, and to able to call them outside the class.
Example:
def params(params):
def params_fn(f):
def params_dec(*args, **kwargs):
for i in params:
f.__setattr__(i, params[i])
return f(*args, **kwargs)
return params_dec
return params_fn
class Channel:
queue = None
# some code & docs omitted
def __init__(self, queue):
self.queue = queue
def start(self):
while True:
if not self.check():
break
class channelA(Channel):
extra_methods = ['get_users', 'get_groups']
def start(self):
# omitted
pass
def __internal_method(self, d):
# omitted
pass
#params({"name": "Get users", "desc": "Get a list of users", "icon": "user-plus"})
def get_users(self):
# omitted
return [i for i in self.queue if i.type = 1]
#params({"name": "Get groups", "desc": "Get a list of groups", "icon": "group-plus"})
def get_groups(self):
# omitted
return [i for i in self.queue if i.type = 2]
q = []
ch = channelA(q)
# some code goes here
# it may be something like
# fns = ch.get_extra_fns()
# or anything similar
for fnName in fns:
print("Callable name:", fnName)
print("Name:", fns[fnName].name)
print("Description:", fns[fnName].desc)
print("Icon ID:", fns[fnName].icon)
print("Result:")
print(ch.call(fns[fnName])())
Is it possible to achieve something like this?
You don't need a wrapper for your decorated methods, since you only set attributes of the method-object. To identify your methods, I would suggest to set a special attribute for these methods, iterate over all class methods and pick those, that have this special method set:
def special(**kw):
def mark(function):
function.is_special = True
function.keywords = kw
return function
return mark
class Channel:
def __init__(self, queue):
self.queue = queue
def start(self):
while True:
if not self.check():
break
class ChannelA(Channel):
def start(self):
# omitted
pass
#special(icon="users.png")
def get_users(self):
# omitted
return [i for i in self.queue if i.type == 1]
#special(hint="don't feed the trolls")
def get_groups(self):
# omitted
return [i for i in self.queue if i.type == 2]
ch = ChannelA(q)
for methodname in dir(type(ch)):
method = getattr(ch, methodname)
if getattr(method, 'is_special', False):
print("Callable name:", method.__name__)
print("Keywords:", method.keywords)
print("Result:", method())
def get_extra_fns(self):
fns = {}
for i in self.extra_methods:
fns['i'] = self.__getattribute__(i)
return fns
Python 3:
import types
import inspect
class A:
def a(self):
pass
class B(A):
def b(self, bool_param, int_param):
pass
b = B()
for member_name in dir(B):
member = getattr(B, member_name)
if isinstance(member, types.FunctionType):
member(b, True) # as illustrated by you... this is hardcoded
argspec = inspect.getargspec(member)
args_for_call = {}
for argname in argspec.args:
if argname == 'bool_param':
args_for_call['bool_param'] = True
if argname == 'int_param':
args_for_call['int_param'] = 3
member(b, **args_for_call) # This is less hardcoded

I cannot understand a case of passing an object as a parameter

I have a class Node with a function defined
class Node(object):
def __init__(self, index, state = None, input = None, directed_neighbours=False):
"""
Parameters
----------
index : int
Node index. Must be unique in the graph.
"""
self._input = input
self.state = state
#self._status = 'active'
self._index = int(index)
self._neighbours = set()
self._port_count = 0
self._ports = []
if directed_neighbours:
self._predecessors = set()
self._successors = self._neighbours
self._directed_neighbours = True
else:
self._successors = self._neighbours
self._predecessors = self._neighbours
self._directed_neighbours = False
#property
def setStatus(self, status):
self._status = status
I have another function
def init(node):
node.setStatus('active')
Now, I have a class
class DistAlgo:
def __init__(self, name, initFunc, states, messages, sendFunc, receiveFunc, stoppingCheck):
self.name = name
#self.inputGraph = inputGraph
self.initFunc = initFunc
self.states = states
self.messages = messages
self.sendFunc = sendFunc
self.receiveFunc = receiveFunc
self.comm_round = 0
self.stoppingCheck = stoppingCheck
def run(self, inputGraph):
for node in inputGraph.nodes:
print('hello', node)
node.state = self.initFunc(node)
<....more code...>
When I create an object of DistAlgo
myalgo = DistAlgo('BMM', init, states, messages, send, receive, stoppingCheck)
and then call its run function:
myalgo.run(problemGraph)
I get an error in the init function above, as:
TypeError: setStatus() missing 1 required positional argument: 'status'
I surely am doing more than one thing wrong I guess, as this is my first Python try. Please point them out!
Properties work a bit differently:
#property
def status(self):
return self._status
#status.setter
def status(self, status):
self._status = status
Now you can set the value with an assignment:
node.status = 'active'

Python: Making inventory, not adding to the container

I'm trying to simulate an inventory, and the debugger isn't finding anything wrong, except
when I run the file to test it, it prints false to an item being added even though it should print true. It's not informing me that there is an error in the code, so I do not know where to look. If you could tell me what I need to change so it will return true at the end (print(sword in bag)) that would be a big help. Ty.
class Item(object):
def __init__(self, name, value, quantity=1):
self.name = name
self.raw = name.strip().lower()
self.quantity = quantity
self.value = value
self.netValue = quantity * value
def recalc(self):
self.netValue = self.quantity * self.value
class Container(object):
def __init__(self, name):
self.name = name
self.inside = {}
def __iter__(self):
return iter(list(self.inside.items()))
def __len__(self):
return len(self.inside)
def __containts__(self, item):
return item.raw in self.inside
def __getitem__(self, item):
return self.inside[item.raw]
def __setitem__(self, item, value):
self.inside[item.raw] = value
return self[item]
def add(self, item, quantity=1):
if quantity < 0:
raise ValueError("Negative Quantity, use remove()")
if item in self:
self[item].quantity += quantity
self[item].recalc()
else:
self[item] = item
def remove(self, item, quantity=1):
if item not in self:
raise KeyError("Not in container")
if quantity < 0:
raise ValueError("Negative quantity, use add() instead")
if self[item].quantity <= quantity:
del self.inside[item.raw]
else:
self[item].quantity -= quantity
self.item.recalc()
bag = Container("BagOfHolding")
sword = Item("Sword", 10) potion = Item("Potion", 5) gold = Item("Gold coin", 1, 50)
bag.add(sword)
print(sword in bag) print(potion in bag)
it looks like you misspelled __contains__ in your method definition.

Categories

Resources