I have a slight complication with my code. I want the pirate attribute to take the value True if the other two attributes are higher than some number when summed up and multiplied by some factor.
For instance, maybe I want the pirate attribute to be True only if social*0.6 + fixed is greater than 5, and false otherwise.
import random
class consumer(object):
"""Initialize consumers"""
def __init__(self, fixed, social,pirate):
self.social = social
self.fixed = fixed
self.pirate = pirate
"""Create an array of people"""
for x in range(1,people):
consumerlist.append(consumer(random.uniform(0,10),random.uniform(0,10),True))
pass
In response to Moses answer: Using a calculated property is safer than calculating the pirate value at initialization only. When decorating a method with the #property attribute, it acts as a property (you don't have to use brackets as is the case for methods), which is always up to date when the social member is changed afterwards.
class Consumer(object):
def __init__(self, fixed, social):
self.fixed = fixed
self.social = social
#property
def pirate(self):
return self.social * 0.6 + self.fixed > 5
consumer1 = Consumer(1, 12)
print("Value of pirate attribute: " + str(consumer1.pirate))
You need to store the random values for fixed and social and then use them for the comparison that generates pirate:
for x in range(1,people):
fixed = random.uniform(0,10)
social = random.uniform(0,10)
pirate = (social * 0.6 + fixed) > 5 # boolean
consumerlist.append(consumer(fixed, social, pirate))
That pass in your for is redundant
Related
I have a class Foo with its instances having a "balance" attribute. I'm designing it in such a way that Foo can track all the balances of its active instances. By active I mean instances that are currently assigned to a declared variable, of part of a List that is a declared variable.
a = Foo(50) # Track this
b = [ Foo(20) for _ in range(5) ] # Track this
Foo(20) # Not assigned to any variable. Do not track this.
Another feature of Foo is that is has an overloaded "add" operator, where you can add two Foo's balances together or add to a Foo's balance by adding it with an int or float.
Example:
x = Foo(200)
x = x + 50
y = x + Foo(30)
Here is my code so far:
from typing import List
class Foo:
foo_active_instances: List = []
def __init__(self, balance: float = 0):
Foo.foo_active_instances.append(self)
self.local_balance: float = balance
#property
def balance(self):
"""
The balance of only this instance.
"""
return self.local_balance
def __add__(self, addend):
"""
Overloading the add operator
so we can add Foo instances together.
We can also add more to a Foo's balance
by just passing a float/int
"""
if isinstance(addend, Foo):
return Foo(self.local_balance + addend.local_balance)
elif isinstance(addend, float | int):
return Foo(self.local_balance + addend)
#classmethod
#property
def global_balance(cls):
"""
Sum up balance of all active Foo instances.
"""
return sum([instance.balance for instance in Foo.foo_active_instances])
But my code has several issues. One problem is when I try to add a balance to an already existing instance, like:
x = Foo(200)
x = x + 50 # Problem: This instantiates another Foo with 200 balance.
y = Foo(100)
# Expected result is 350, because 250 + 100 = 350.
# Result is 550
# even though we just added 50 to x.
print(Foo.global_balance)
Another problem is replacing a Foo instance with None doesn't remove it from Foo.foo_active_instances.
k = Foo(125)
k = None
# Expected global balance is 0,
# but the balance of the now non-existing Foo still persists
# So result is 125.
print(Foo.global_balance)
I tried to make an internal method that loops through foo_active_instances and counts how many references an instance has. The method then pops the instance from foo_active_instance if it doesn't have enough. This is very inefficient because it's a loop and it's called each time a Foo instance is made and when the add operator is used.
How do I rethink my approach? Is there a design pattern just for this problem? I'm all out of ideas.
The weakref module is perfect for this design pattern. Instead of making foo_active_instances a list, you can make it a weakref.WeakSet. This way, when a Foo object's reference count falls to zero (e.g., because it wasn't bound to a variable), it will be automatically removed from the set.
class Foo:
foo_active_instances = weakref.WeakSet()
def __init__(self, balance: float = 0) -> None:
Foo.foo_active_instances.add(self)
...
In order to add Foo objects to a set, you'll have to make them hashable. Maybe something like
class Foo:
...
def __hash__(self) -> int:
return hash(self.local_balance)
You can use inspect to check if the __init__ or __add__ methods have been called as part of an assignment statement. Additionally, you can keep a default parameter in __init__ to prevent increasing your global sum by the value passed to it when creating a new Foo object from __add__:
import inspect, re
def from_assignment(frame):
return re.findall('[^\=]\=[^\=]', inspect.getframeinfo(frame).code_context[0])
class Foo:
global_balance = 0
def __init__(self, balance, block=False):
if not block and from_assignment(inspect.currentframe().f_back):
Foo.global_balance += balance
self.local_balance = balance
def __add__(self, obj):
if from_assignment(inspect.currentframe().f_back) and not hasattr(obj, 'local_balance'):
Foo.global_balance += obj
return Foo(getattr(obj, 'local_balance', obj), True)
a = Foo(50)
b = [Foo(20) for _ in range(5)]
Foo(20)
print(Foo.global_balance) #150
x = Foo(200)
x = x + 50
y = Foo(100)
print(Foo.global_balance) #350
I have the class below where a method changes its property. However, I need the original unaltered property. What's the idiomatic way to do this?
The class describes a Borrower. The borrower has a property called PMT. this is the amortized value of the borrower. It is calculated from the present value. However, the borrower has a method which applies interest. This changes the present value, which in turn changes the PMT. I need to original PMT after I have applied the interest. What's the best way to get around it.
Heres the sample of the code
import numpy as np
class Borrower:
def __init__(self, present_value, term, rate):
self.present_value = present_value
self.term = term
self.rate = rate
def pmt(self):
return -np.pmt(self.rate/12, self.term, self.present_value)
def apply_interest(self):
self.present_value *= 1 + self.rate
Heres the problem:
b = Borrower(1000, 12, 0.1)
b.pmt() # 87.91
b.apply_interest()
b.pmt() # 96.70 I need 87.91 here!
Should I create a Borrower with an initial pmt like this?
class Borrower:
def __init__(self, present_value, term, rate):
self.present_value = present_value
self.term = term
self.rate = rate
self.init_pmt = self.pmt()
I'm working under python pyramid, with Python3.
I have a model that looks like this:
class OneTimeCode(Base):
__tablename__ = 'otc_one_time_codes'
otc_one_time_code_id = Column(Integer, primary_key=True)
otc_one_time_code = Column(String(32))
otc_usr_user_id = Column(Integer, ForeignKey('usr_users.usr_user_id'), nullable=True)
otc_expire_time = Column(DateTime)
def __init__(self, otc_usr_user_id, otc_expire_time=None):
self.otc_usr_user_id = otc_usr_user_id
if otc_expire_time is None:
self.otc_expire_time = (datetime.now() + timedelta(6*365/12)).isoformat()
else:
self.otc_expire_time = otc_expire_time
#classmethod
def get_code(self, hlength=6):
seed = datetime.now() + timedelta(random.randrange(1,10000))
tmp_hash = hashlib.md5(seed.strftime("%Y-%m-%d %H:%M:%S.%F").encode('utf-8')).hexdigest()
if hlength == 32:
self.otc_one_time_code = tmp_hash
else:
self.otc_one_time_code = tmp_hash[0 : hlength]
print(self.otc_one_time_code)
The problem is, when I instantiate one of these objects and then explicitly call get_code, the print line at the end prints to the screen the code successfully.
However, in my view, if I explicitly try to print that property, it's 'None'
Here's what my view code looks like:
otc = OneTimeCode(
otc_usr_user_id = user.usr_user_id
)
otc.get_code()
pprint.pprint(vars(otc))
session.add(otc)
And the console output looks like this:
0d097c
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x50877d0>, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] INSERT INTO otc_one_time_codes (otc_one_time_code, otc_usr_user_id, otc_expire_time) VALUES (%(otc_one_time_code)s, %(otc_usr_user_id)s, %(otc_expire_time)s) RETURNING otc_one_time_codes.otc_one_time_code_id 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] {'otc_one_time_code': None, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,247
INFO [sqlalchemy.engine.base.Engine][Dummy-2] COMMIT
You can see the value inside the model: 0d097c, and also the pprint object, where it doesn't look like the property exists.
Why can't I get access to this property?
Looks like you should be using a #property instead of a OTC, however it also seems like this may be something you DON'T want to calculate each time!
# for all the docstrings, let multi = Multi(2)
class Multi(object):
def __init__(self, attribute):
"""When instantiated, set self.attribute to attribute"""
self.attribute = attribute
#property
def attribute_times_ten(self):
"""accessed via multi.attribute_times_ten
and will return 20. Use properties to signify
a variable that requires some work done to it
that needs to calculated each time it's called."""
return attribute_times_ten
#classmethod
def times_ten(cls, num):
"""Not the best example, but a #classmethod will
give the class as its first argument, NOT the
instance. This is useful in lots of constructor
settings, e.g. CreateClass.fromstring("attributes")"""
return num * 5
def generate_number(self, multiplier):
"""This is just a normal method. This is what I think
you want, tbh, and you should probably call it in your
__init__ method since you NEED this to run in your OTC
for it to work as intended. Methods (like properties)
are automagically passed the instance as the first
argument, so we can CHANGE self.attribute with that."""
self.attribute = self.attribute * multiplier
Docstrings should be self descriptive, but:
multi = Multi(2)
multi.attribute_times_ten # returns 20
Multi.times_ten(8) # returns 80, note the capital M!
multi.generate_number(3) # self.attribute is now 6
multi.attribute_times_ten # returns 60
A real-world case where you might need all of the above:
class _Tile(object):
def __init__(self, x, y):
"""A naive implementation of Tile that doesn't care
what its side length is and doesn't have any properties
to hide its attributes"""
self.x = x
self.y = y
#classmethod
def tiles_to_pixels(cls, tile):
return cls(tile._x * tile.side_length, tile._y * tile.side_length)
#classmethod
def tiles_to_tiles(cls, tile):
return cls(tile._x, tile._y)
class Tile(object):
def __init__(self, x, y, side_length):
"""A tile object in a map"""
self._x = x # x-coord in tiles
self._y = y # y-coord in tiles
self.side_length = side_length # pixels per tile
#property
def in_pixels(self):
"""self.in_pixels returns an object whose .x and .y
correspond to the x and y position IN PIXELS of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_pixels(self)
return _tile
#property
def in_tiles(self):
"""self.in_tiles returns an object whose .x and .y
correspond to the x and y position IN TILES of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_tiles(self)
return _tile
def change_side_length(self, new_length):
"""Use to change the side length. This can break
your whole map since it's naive, so be careful."""
self.side_length = new_length
my_tile = Tile(0,0,32) # 32 pixel tile starting at (0,0)
my_tile.x # NameError, since it's called my_tile._x
my_tile.in_tiles.x # 0
my_tile.in_pixels.y # 0
other_tile = Tile(4,7,32) # 32 pixel tile starting at (4,7)
other_tile.y # NameError, see above
other_tile.in_tiles.y # 7
other_tile.in_pixels.x # 128
I would like to implement something that would work like this:
memo = Note("memo",5)
report = Note("report",20)
notebook = Notebook(memo,report)
print str(notebook.memo) # 5 expected
print str(notebook.report) # 20 expected
Inspired by:
http://znasibov.info/blog/html/2010/03/10/python-classes-dynamic-properties.html
and
How to implement property() with dynamic name (in python)
, I implemented the following code:
class Note:
def __init__(self,name,size):
self.name = name
self.size = size
class Notebook(object):
def __new__(cls,*notes):
notebook = object.__new__(cls)
setattr(notebook,'_notes',{note.name : note.size for note in notes})
functions = [lambda notebook : notebook._notes[note.name] for note in notes]
for note,function in zip(notes,functions) :
#version 1
setattr(notebook.__class__, note.name, property(function))
#version 2 -- note : __class__ removed
#setattr(notebook, note.name, property(function))
return notebook
note: I know for this minimal code use of __new__ instead of __init__ is not justified, but this will be required later on when I use subclasses of Notebook
If I use version 1:
1. instead of having 5 and 20 being printed, it prints 20 and 20. I do not get why. Printing the functions shows an array of functions with different addresses.
2. I used __class__ inspired by the blog entry given above, but I am not sure what it does. It makes the property a class property ? (which would be real bad in my case)
If I use version 2:
prints something like property object at 0x7fb86a9d9b50.
This seems to make sense, but I am not sure I understand why it does not print the same thing for version 1.
Is there a way to fix this, using either version (or another completely different approach) ?
Edit
An interesting answer for solving the issue was proposed. Here the corresponding code:
class Note:
def __init__(self,name,value):
self.name = name
self.size = value
def _get_size(self,notebook_class=None): return self.size+1
class Notebook(object):
def __new__(cls,*notes):
notebook = object.__new__(cls)
notebook._notes = {note.name : note.size for note in notes}
for note in notes : setattr(notebook.__class__, note.name, property(note._get_size))
return notebook
Issue is : now this test code is not giving the desired output:
memo1 = Note("memo",5)
report1 = Note("report",20)
notebook1 = Notebook(memo1,report1)
print str(notebook1.memo) # print 6 as expected (function of Note return size+1)
print str(notebook1.report) # print 21 as expected
memo2 = Note("memo",35)
report2 = Note("report",40)
notebook2 = Notebook(memo2,report2)
print str(notebook2.memo) # print 36 as expected
print str(notebook2.report) # print 41 expected
print str(notebook1.memo) # does not print 6 but 36 !
print str(notebook1.report) # does not print 21 but 41 !
I guess this was to be expected as the property was added to the class ....
Anyway to overcome this issue ?
Some more food for though. To simply obtain what you want to do in your first set of code, you can do that without all the extra tricks.
The simplest way to do it is set the attributes to the desired one directly. (code consolidated in improper manors simply to save space)
class Note:
def __init__(self, name, value): self.name, self._size = name, value
size = property(lambda x: x._size+1)
class Notebook(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
notebook._notes = {note.name: note.size for note in notes}
for note in notes: setattr(notebook, note.name, note.size)
return notebook
memo1, report1 = Note("memo", 5), Note("report", 20)
notebook1 = Notebook(memo1, report1)
print(notebook1.memo, notebook1.report) # 6 21
memo2, report2 = Note("memo", 35), Note("report", 40)
notebook2 = Notebook(memo2,report2)
print(notebook2.memo, notebook2.report) # 36 41
print(notebook1.memo, notebook1.report) # 6 21
notebook1.memo += 5
print(notebook1.memo) # 11
print(memo1.size) # 6
memo1.size += 5 # AttributeError: can't set attribute
The second way would be to have the notebook literally be a container for all the notes you pass to it. This way it would simply update the original class objects, and is basically just a holder for them.
class Note2(object):
def __init__(self, name, value): self.name, self._size = name, value
def _set_size(self, value): self._size = value
size = property(lambda x: x._size+1, _set_size)
def __repr__(self): return str(self.size) #simple trick to gain visual access to .size
class Notebook2(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
notebook._notes = {note.name: note.size for note in notes}
for note in notes: setattr(notebook, note.name, note)
return notebook
memo1, report1 = Note2("memo", 5), Note2("report", 20)
notebook1 = Notebook2(memo1, report1)
print(notebook1.memo, notebook1.report) # 6 21
memo2, report2 = Note2("memo", 35), Note2("report", 40)
notebook2 = Notebook2(memo2, report2)
print( notebook2.memo, notebook2.report) # 36 41
print(notebook1.memo, notebook1.report) # 6 21
notebook1.memo.size += 16
print(notebook1.memo) # 23
print(memo1) # 23, Notice this will also set the original objects value to the new value as well
notebook1.memo += 15 # TypeError: unsupported operand type(s) for +=: 'Note2' and 'int' - It is true without making it as a property does make it less effective to work with
It should also be possible to do as in your provided link suggests to make each Note class a member of Notebook with a leading underscore (i.e. notebook._memo) and then make a property for Notebook which would link Note name to size (i.e. notebook.memo would be a link to notebook._memo.size). Hope these examples help.
Original answer.
Interesting idea, to simply get it working here is a hack of your original version:
class Note(object):
def __init__(self,name, size):
self.name = name
self._size = size
def _get_size(self, notebook_class=None):
return self._size
def _set_size(self, notebook_class=None, size=0):
self._size = size
class Notebook(object):
def __new__(cls,*notes):
notebook = object.__new__(cls)
for note in notes:
setattr(notebook.__class__, note.name, property(note._get_size, note._set_size))
return notebook
However you seem to be removing each Note class when you ingest them into Notebook anyways so you could do something much easier:
class Note(object):
def __init__(self, name, size):
self.name = name
self.size = size
class Notebook(object):
def __new__(cls, *notes):
notebook = object.__new__(cls)
for note in notes:
setattr(notebook.__class__, note.name, note.size)
return notebook
To be any more helpful I would really need to know the goal or a general idea of where you want to take this. It seems confusing to set the properties in such an odd way, yet only do it once at the creation of the class as opposed to the examples of being able to dynamical add and remove them.
Hope this helped
Creating functions in a loop is tricky:
>>> lambdas = [(lambda: i) for i in range(5)]
>>> for lamb in lambdas:
... print(lamb())
...
4
4
4
4
4
Note that all lambdas refer to the value that i assumed in the last iteration.
When you create a function python associates a closure to it, which tells the interpreter which non local variables the function should use:
>>> lambdas[0].__closure__[0]
<cell at 0x7f675ab2dc90: int object at 0x9451e0>
However it refers to the variable, not the actual object contained when the function was defined. This would require a more complicated handling of the function frames.
this means that following iterations change the value contained in this cell, and in the end only the last iteration is significant:
>>> lambdas[0].__closure__[0].cell_contents
4
If you want to refer to previous values you can use a default value for an argument:
>>> lambdas = [(lambda i=i: i) for i in range(5)]
>>> for lamb in lambdas:
... print(lamb())
...
0
1
2
3
4
Concerning the second version. property is implemented as a descriptor (see also this answer) and hence it must be set in the class in order for it to work properly. The same is true for other decorators such as staticmethod and classmethod. Putting them in an instance will just return the property object, as you observed.
The line:
setattr(notebook,'_notes',{note.name : note.size for note in notes})
Can be safely changed to the simpler and more readable:
notebook._notes = {note.name : note.size for note in notes}
I have been subclassing an Python's random number generator to make a generator that doesn't repeat results (it's going to be used to generate unique id's for a simulator) and I was just testing to see if it was consistent in it's behavior after it has been loaded from a previours state
Before people ask:
It's a singleton class
No there's nothing else that should be using that instance (a tear down sees to that)
Yes I tested it without the singleton instance to check
and yes when I create this subclass I do call a new instance ( super(nrRand,self).__init__())
And yes according to another post I should get consistent results see: Rolling back the random number generator in python?
Below is my test code:
def test_stateSavingConsitantcy(self):
start = int(self.r.random())
for i in xrange(start):
self.r.random()
state = self.r.getstate()
next = self.r.random()
self.r.setstate(state)
nnext = self.r.random()
self.assertEqual(next, nnext, "Number generation not constant got {0} expecting {1}".format(nnext,next))
Any help that can be provided would greatly appreciated
EDIT:
Here is my subclass as requested
class Singleton(type):
_instances = {}
def __call__(self, *args, **kwargs):
if self not in self._instances:
self._instances[self] = super(Singleton,self).__call__(*args,**kwargs)
return self._instances[self]
class nrRand(Random):
__metaclass__ = Singleton
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
super(nrRand,self).__init__()
self.previous = []
def random(self):
n = super(nrRand,self).random()
while n in self.previous:
n = super(nrRand,self).random()
self.previous.append(n)
return n
def seed(self,x):
if x is None:
x = long(time.time()*1000)
self.previous = []
count = x
nSeed = 0
while count < 0:
nSeed = super(nrRand,self).random()
count -= 1
super(nrRand,self).seed(nSeed)
while nSeed < 0:
super(nrRand,self).seed(nSeed)
count -= 1
def getstate(self):
return (self.previous, super(nrRand,self).getstate())
def setstate(self,state):
self.previous = state[0]
super(nrRand,self).setstate(state[1])
getstate and setstate only manipulate the state the Random class knows about; neither method knows that you also need to roll back the set of previously-generated numbers. You're rolling back the state inherited from Random, but then the object sees that it's already produced the next number and skips it. If you want getstate and setstate to work properly, you'll have to override them to set the state of the set of already-generated numbers.
UPDATE:
def getstate(self):
return (self.previous, super(nrRand,self).getstate())
This shouldn't directly use self.previous. Since you don't make a copy, you're returning the actual object used to keep track of what numbers have been produced. When the RNG produces a new number, the state returned by getstate reflects the new number. You need to copy self.previous, like so:
def getstate(self):
return (self.previous[:], super(nrRand, self).getstate())
I also recommend making a copy in setstate:
def setstate(self, state):
previous, parent_state = state
self.previous = previous[:]
super(nrRand, self).setstate(parent_state)