How to set an attirbutes list when creating new object - python

Hope the title was not too confusing. I've been working on program and I have created so attribut with a default list in it. The thing is I don't know how to do modification to that list when I create this object. Thank you
def __init__(self,nm,pm,adss):
self._nom = nm
self._prenom = pm
self._adresse = adss
self._bonus = [0,0,0,0]```
employer1 = Employe("nom","prenom","adresse",[0,250,50,10] #For exemple I wanted to set
#the list for that one. But im not sure if I can do it like that or if I might need to create a new function for it.

The current version would not work, since you are not passing that list as a parameter to the initialisation function. Your __init__ function would need to look like this:
def __init__(self,nm,pm,adss,bns):
self._nom = nm
self._prenom = pm
self._adresse = adss
self._bonus = bns
Then the initialiser knows that it should expect a fourth parameter to apply to the _bonus attribute.
Note that this will only set the attribute when creating the object. To change it once the object is created, you should create a setter function.
I'm not certain from your question if you want a default set for the bonus as standard. If so, see the comments on your question for details on how to do that.

You could give an immutable default value to the bonus argument, then assign to self._bonus depending on whether or not a value was passed.
It is usually good practice to make a copy of the mutable parameters passed as argument to an object.
def __init__(self, nom, prenom, adresse, bonus=None):
self._nom = nom
self._prenom = prenom
self._adresse = adresse
self._bonus = [] if bonus is None else bonus[:] # make a copy of the mutable parameter

def __init__(self, nom, prenom, adresse, bonus=None):
self._nom = nom
self._prenom = prenom
self._adresse = adresse
self._bonus = bonus if bonus else [0, 0, 0, 0]
Reasons that this answers the question:
You don't want to make a list literal (one of these: []) be the default parameter to a function/method call. This is the immutable default value bug everyone's talking about.
Setting the bonus parameter's default to None and then performing a check to see if a different value was passed is the way to go.
The cleanest way to make a check on the value of the bonus parameter is shown above.

Related

What is the difference in declaring attributes in the constructor in the below 3 ways?

def __init__(self):
self.score=[]
def __init__(self,score=[]):
self.score=score
def __init__(self,score):
self.score=[]
Can you please explain what happens behind the scenes? TIA.
def init(self):
self.score=[]
self.score is always initialized to an empty list.
def init(self,score=[]):
self.score=score
The caller can pass in an optional list parameter. If they don't, self.score is initialized to an empty list once, when the function is defined. (See Common Gotchas: Mutable Default Arguments for why this is a bad idea.)
def init(self,score):
self.score=[]
The caller must pass in a list parameter (normally, that would be assigned to self.score, but you're throwing that value out and setting self.score to an empty list). The third example probably should be:
def init(self, score):
self.score = score
The "score" member is always set to []
You have the option to specify what you want to set the member "score" to.
You have to specify "score" while constructing the object. The difference to 2. is, that here you have to specify it, while in 2. it's optional, and you're just setting a default
In the first one, you always assign [] to the objects score variable.
In the second one, you can construct the object with or without the score value, for example if you do obj = object([1,2,3]), then it will have score equal to [1,2,3], but you can also call it without the score argument, like obj = object(), and it will have score equal to [].
In the third example, you have to create with the score argument, but it will be ignored, as it is not assigned to the self.score variable.
score in function header is a local variable
def init(self,score):
and here it is an instance attribute
self.score=[]
The first and last methods have exactly the same effect: assign an empty list to the instance attribute score. For some strange reason, the third one requires an extra positional parameter score, which is not used.
The middle one creates and reserves a default value for the parameter score; at parse time, this value is an empty list. Any instantiation that omits that argument gets assigned the reserved list. The danger here is that these instantiations will all refer to the same list: a change to the score attribute in one will be reflected in all of the other objects that used the default value.

Getting the name of a variable as a string

I already read How to get a function name as a string?.
How can I do the same for a variable? As opposed to functions, Python variables do not have the __name__ attribute.
In other words, if I have a variable such as:
foo = dict()
foo['bar'] = 2
I am looking for a function/attribute, e.g. retrieve_name() in order to create a DataFrame in Pandas from this list, where the column names are given by the names of the actual dictionaries:
# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [retrieve_name(d) for d in list_of_dicts]
With Python 3.8 one can simply use f-string debugging feature:
>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo'
One drawback of this method is that in order to get 'foo' printed you have to add f'{foo=}' yourself. In other words, you already have to know the name of the variable. In other words, the above code snippet is exactly the same as just
>>> 'foo'
Even if variable values don't point back to the name, you have access to the list of every assigned variable and its value, so I'm astounded that only one person suggested looping through there to look for your var name.
Someone mentioned on that answer that you might have to walk the stack and check everyone's locals and globals to find foo, but if foo is assigned in the scope where you're calling this retrieve_name function, you can use inspect's current frame to get you all of those local variables.
My explanation might be a little bit too wordy (maybe I should've used a "foo" less words), but here's how it would look in code (Note that if there is more than one variable assigned to the same value, you will get both of those variable names):
import inspect
x, y, z = 1, 2, 3
def retrieve_name(var):
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
return [var_name for var_name, var_val in callers_local_vars if var_val is var]
print(retrieve_name(y))
If you're calling this function from another function, something like:
def foo(bar):
return retrieve_name(bar)
foo(baz)
And you want the baz instead of bar, you'll just need to go back a scope further. This can be done by adding an extra .f_back in the caller_local_vars initialization.
See an example here: ideone
The only objects in Python that have canonical names are modules, functions, and classes, and of course there is no guarantee that this canonical name has any meaning in any namespace after the function or class has been defined or the module imported. These names can also be modified after the objects are created so they may not always be particularly trustworthy.
What you want to do is not possible without recursively walking the tree of named objects; a name is a one-way reference to an object. A common or garden-variety Python object contains no references to its names. Imagine if every integer, every dict, every list, every Boolean needed to maintain a list of strings that represented names that referred to it! It would be an implementation nightmare, with little benefit to the programmer.
TL;DR
Use the Wrapper helper from python-varname:
from varname.helpers import Wrapper
foo = Wrapper(dict())
# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2
For list comprehension part, you can do:
n_jobs = Wrapper(<original_value>)
users = Wrapper(<original_value>)
queues = Wrapper(<original_value>)
priorities = Wrapper(<original_value>)
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# REMEMBER that you have to access the <original_value> by d.value
I am the author of the python-varname package. Please let me know if you have any questions or you can submit issues on Github.
The long answer
Is it even possible?
Yes and No.
We are retrieving the variable names at runtime, so we need a function to be called to enable us to access the previous frames to retrieve the variable names. That's why we need a Wrapper there. In that function, at runtime, we are parsing the source code/AST nodes in the previous frames to get the exact variable name.
However, the source code/AST nodes in the previous frames are not always available, or they could be modified by other environments (e.g: pytest's assert statement). One simple example is that the codes run via exec(). Even though we are still able to retrieve some information from the bytecode, it needs too much effort and it is also error-prone.
How to do it?
First of all, we need to identify which frame the variable is given. It's not always simply the direct previous frame. For example, we may have another wrapper for the function:
from varname import varname
def func():
return varname()
def wrapped():
return func()
x = wrapped()
In the above example, we have to skip the frame inside wrapped to get to the right frame x = wrapped() so that we are able to locate x. The arguments frame and ignore of varname allow us to skip some of these intermediate frames. See more details in the README file and the API docs of the package.
Then we need to parse the AST node to locate where the variable is assigned value (function call) to. It's not always just a simple assignment. Sometimes there could be complex AST nodes, for example, x = [wrapped()]. We need to identify the correct assignment by traversing the AST tree.
How reliable is it?
Once we identify the assignment node, it is reliable.
varname is all depending on executing package to look for the node. The node executing detects is ensured to be the correct one (see also this).
It partially works with environments where other AST magics apply, including pytest, ipython, macropy, birdseye, reticulate with R, etc. Neither executing nor varname is 100% working with those environments.
Do we need a package to do it?
Well, yes and no, again.
If your scenario is simple, the code provided by #juan Isaza or #scohe001 probably is enough for you to work with the case where a variable is defined at the direct previous frame and the AST node is a simple assignment. You just need to go one frame back and retrieve the information there.
However, if the scenario becomes complicated, or we need to adopt different application scenarios, you probably need a package like python-varname, to handle them. These scenarios may include to:
present more friendly messages when the source code is not available or AST nodes are not accessible
skip intermediate frames (allows the function to be wrapped or called in other intermediate frames)
automatically ignores calls from built-in functions or libraries. For example: x = str(func())
retrieve multiple variable names on the left-hand side of the assignment
etc.
How about the f-string?
Like the answer provided by #Aivar Paalberg. It's definitely fast and reliable. However, it's not at runtime, meaning that you have to know it's foo before you print the name out. But with varname, you don't have to know that variable is coming:
from varname import varname
def func():
return varname()
# In external uses
x = func() # 'x'
y = func() # 'y'
Finally
python-varname is not only able to detect the variable name from an assignment, but also:
Retrieve variable names directly, using nameof
Detect next immediate attribute name, using will
Fetch argument names/sources passed to a function using argname
Read more from its documentation.
However, the final word I want to say is that, try to avoid using it whenever you can.
Because you can't make sure that the client code will run in an environment where the source node is available or AST node is accessible. And of course, it costs resources to parse the source code, identify the environment, retrieve the AST nodes and evaluate them when needed.
On python3, this function will get the outer most name in the stack:
import inspect
def retrieve_name(var):
"""
Gets the name of var. Does it from the out most frame inner-wards.
:param var: variable to get name from.
:return: string
"""
for fi in reversed(inspect.stack()):
names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
if len(names) > 0:
return names[0]
It is useful anywhere on the code. Traverses the reversed stack looking for the first match.
I don't believe this is possible. Consider the following example:
>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664
The a and b point to the same object, but the object can't know what variables point to it.
def name(**variables):
return [x for x in variables]
It's used like this:
name(variable=variable)
>> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v == my_var][0]
>> my_var_name
'my_var'
In case you get an error if myvar points to another variable, try this (suggested by #mherzog)-
>> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v is my_var][0]
>> my_var_name
'my_var'
locals() - Return a dictionary containing the current scope's local variables.
by iterating through this dictionary we can check the key which has a value equal to the defined variable, just extracting the key will give us the text of variable in string format.
from (after a bit changes)
https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python
I wrote the package sorcery to do this kind of magic robustly. You can write:
from sorcery import dict_of
columns = dict_of(n_jobs, users, queues, priorities)
and pass that to the dataframe constructor. It's equivalent to:
columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)
Here's one approach. I wouldn't recommend this for anything important, because it'll be quite brittle. But it can be done.
Create a function that uses the inspect module to find the source code that called it. Then you can parse the source code to identify the variable names that you want to retrieve. For example, here's a function called autodict that takes a list of variables and returns a dictionary mapping variable names to their values. E.g.:
x = 'foo'
y = 'bar'
d = autodict(x, y)
print d
Would give:
{'x': 'foo', 'y': 'bar'}
Inspecting the source code itself is better than searching through the locals() or globals() because the latter approach doesn't tell you which of the variables are the ones you want.
At any rate, here's the code:
def autodict(*args):
get_rid_of = ['autodict(', ',', ')', '\n']
calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
calling_code = calling_code[calling_code.index('autodict'):]
for garbage in get_rid_of:
calling_code = calling_code.replace(garbage, '')
var_names, var_values = calling_code.split(), args
dyn_dict = {var_name: var_value for var_name, var_value in
zip(var_names, var_values)}
return dyn_dict
The action happens in the line with inspect.getouterframes, which returns the string within the code that called autodict.
The obvious downside to this sort of magic is that it makes assumptions about how the source code is structured. And of course, it won't work at all if it's run inside the interpreter.
This function will print variable name with its value:
import inspect
def print_this(var):
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
***Input & Function call:***
my_var = 10
print_this(my_var)
***Output**:*
my_var: 10
>>> locals()['foo']
{}
>>> globals()['foo']
{}
If you wanted to write your own function, it could be done such that you could check for a variable defined in locals then check globals. If nothing is found you could compare on id() to see if the variable points to the same location in memory.
If your variable is in a class, you could use className.dict.keys() or vars(self) to see if your variable has been defined.
I have a method, and while not the most efficient...it works! (and it doesn't involve any fancy modules).
Basically it compares your Variable's ID to globals() Variables' IDs, then returns the match's name.
def getVariableName(variable, globalVariables=globals().copy()):
""" Get Variable Name as String by comparing its ID to globals() Variables' IDs
args:
variable(var): Variable to find name for (Obviously this variable has to exist)
kwargs:
globalVariables(dict): Copy of the globals() dict (Adding to Kwargs allows this function to work properly when imported from another .py)
"""
for globalVariable in globalVariables:
if id(variable) == id(globalVariables[globalVariable]): # If our Variable's ID matches this Global Variable's ID...
return globalVariable # Return its name from the Globals() dict
In Python, the def and class keywords will bind a specific name to the object they define (function or class). Similarly, modules are given a name by virtue of being called something specific in the filesystem. In all three cases, there's an obvious way to assign a "canonical" name to the object in question.
However, for other kinds of objects, such a canonical name may simply not exist. For example, consider the elements of a list. The elements in the list are not individually named, and it is entirely possible that the only way to refer to them in a program is by using list indices on the containing list. If such a list of objects was passed into your function, you could not possibly assign meaningful identifiers to the values.
Python doesn't save the name on the left hand side of an assignment into the assigned object because:
It would require figuring out which name was "canonical" among multiple conflicting objects,
It would make no sense for objects which are never assigned to an explicit variable name,
It would be extremely inefficient,
Literally no other language in existence does that.
So, for example, functions defined using lambda will always have the "name" <lambda>, rather than a specific function name.
The best approach would be simply to ask the caller to pass in an (optional) list of names. If typing the '...','...' is too cumbersome, you could accept e.g. a single string containing a comma-separated list of names (like namedtuple does).
I think it's so difficult to do this in Python because of the simple fact that you never will not know the name of the variable you're using. So, in his example, you could do:
Instead of:
list_of_dicts = [n_jobs, users, queues, priorities]
dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}
Many of the answers return just one variable name. But that won't work well if more than one variable have the same value. Here's a variation of Amr Sharaki's answer which returns multiple results if more variables have the same value.
def getVariableNames(variable):
results = []
globalVariables=globals().copy()
for globalVariable in globalVariables:
if id(variable) == id(globalVariables[globalVariable]):
results.append(globalVariable)
return results
a = 1
b = 1
getVariableNames(a)
# ['a', 'b']
just another way to do this based on the content of input variable:
(it returns the name of the first variable that matches to the input variable, otherwise None. One can modify it to get all variable names which are having the same content as input variable)
def retrieve_name(x, Vars=vars()):
for k in Vars:
if isinstance(x, type(Vars[k])):
if x is Vars[k]:
return k
return None
If the goal is to help you keep track of your variables, you can write a simple function that labels the variable and returns its value and type. For example, suppose i_f=3.01 and you round it to an integer called i_n to use in a code, and then need a string i_s that will go into a report.
def whatis(string, x):
print(string+' value=',repr(x),type(x))
return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)
## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)
This prints to the window at each call for debugging purposes and also yields a string for the written report. The only downside is that you have to type the variable twice each time you call the function.
I am a Python newbie and found this very useful way to log my efforts as I program and try to cope with all the objects in Python. One flaw is that whatis() fails if it calls a function described outside the procedure where it is used. For example, int(i_f) was a valid function call only because the int function is known to Python. You could call whatis() using int(i_f**2), but if for some strange reason you choose to define a function called int_squared it must be declared inside the procedure where whatis() is used.
Maybe this could be useful:
def Retriever(bar):
return (list(globals().keys()))[list(map(lambda x: id(x), list(globals().values()))).index(id(bar))]
The function goes through the list of IDs of values from the global scope (the namespace could be edited), finds the index of the wanted/required var or function based on its ID, and then returns the name from the list of global names based on the acquired index.
Whenever I have to do it, mostly while communicating json schema and constants with the frontend I define a class as follows
class Param:
def __init__(self, name, value):
self.name = name
self.value = value
Then define the variable with name and value.
frame_folder_count = Param({'name':'frame_folder_count', 'value':10})
Now you can access the name and value using the object.
>>> frame_folder_count.name
'frame_folder_count'
>>> def varname(v, scope=None):
d = globals() if not scope else vars(scope); return [k for k in d if d[k] == v]
...
>>> d1 = {'a': 'ape'}; d2 = {'b': 'bear'}; d3 = {'c': 'cat'}
>>> ld = [d1, d2, d3]
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3']]
>>> d5 = d3
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3', 'd5']]
>>> def varname(v, scope=None):
d = globals() if not scope else vars(scope); return [k for k in d if d[k] is v]
...
>>> [varname(d) for d in ld]
[['d1'], ['d2'], ['d3', 'd5']]
As you see and is noted here, there can be multiple variables with the same value or even address, so using a wrapper to keep the names with the data is best.
Following method will not return the name of variable but using this method you can create data frame easily if variable is available in global scope.
class CustomDict(dict):
def __add__(self, other):
return CustomDict({**self, **other})
class GlobalBase(type):
def __getattr__(cls, key):
return CustomDict({key: globals()[key]})
def __getitem__(cls, keys):
return CustomDict({key: globals()[key] for key in keys})
class G(metaclass=GlobalBase):
pass
x, y, z = 0, 1, 2
print('method 1:', G['x', 'y', 'z']) # Outcome: method 1: {'x': 0, 'y': 1, 'z': 2}
print('method 2:', G.x + G.y + G.z) # Outcome: method 2: {'x': 0, 'y': 1, 'z': 2}
A = [0, 1]
B = [1, 2]
pd.DataFrame(G.A + G.B) # It will return a data frame with A and B columns
Some of the previous cases would fail if there are two variables with the same value. So it is convenient to alert it:
Defining function:
# Variable to string of variable name
def var_name(variable,i=0):
results = []
for name in globals():
if eval(name) == variable:
results.append(name)
if len(results) > 1:
print('Warning:' )
print(' var_name() has found',len(results), 'possible outcomes.')
print(' Please choose the suitable parameter "i". Where "i" is the index')
print(' that matches your choice from the list below.')
print(' ',results) ; print('')
return results[i]
Use:
var_1 = 10
var_name(var_1) # Output will be "var_1"
If you have 2 variables with the same value like var_1 = 8 and var_2 = 8, then a warning will appear.
var_1 = 8
var_2 = 8
var_name(var_2) # Output will be "var_1" too but Warning will appear
You can get your variable as kwargs and return it as string:
var=2
def getVarName(**kwargs):
return list(kwargs.keys())[0]
print (getVarName(var = var))
Note: variable name must be equal to itself.
I try to get name from inspect locals, but it cann't process var likes a[1], b.val.
After it, I got a new idea --- get var name from the code, and I try it succ!
code like below:
#direct get from called function code
def retrieve_name_ex(var):
stacks = inspect.stack()
try:
func = stacks[0].function
code = stacks[1].code_context[0]
s = code.index(func)
s = code.index("(", s + len(func)) + 1
e = code.index(")", s)
return code[s:e].strip()
except:
return ""
You can try the following to retrieve the name of a function you defined (does not work for built-in functions though):
import re
def retrieve_name(func):
return re.match("<function\s+(\w+)\s+at.*", str(func)).group(1)
def foo(x):
return x**2
print(retrieve_name(foo))
# foo
When finding the name of a variable from its value,
you may have several variables equal to the same value,
for example var1 = 'hello' and var2 = 'hello'.
My solution:
def find_var_name(val):
dict_list = []
global_dict = dict(globals())
for k, v in global_dict.items():
dict_list.append([k, v])
return [item[0] for item in dict_list if item[1] == val]
var1 = 'hello'
var2 = 'hello'
find_var_name('hello')
Outputs
['var1', 'var2']
Compressed version of iDilip's answer:
import inspect
def varname(x):
return [k for k,v in inspect.currentframe().f_back.f_locals.items() if v is x][0]
hi = 123
print(varname(hi))
It's totally possible to get the name of an instance variable, so long as it is the property of a class.
I got this from Effective Python by Brett Slatkin. Hope it helps someone:
The class must implement the get, set, and set_name dunder methods, which are part of the "Descriptor Protocol"
This worked when I ran it:
class FieldThatKnowsItsName():
def __init__(self):
self.name = None
self._value= None
self.owner = None
def __set_name__(self, owner, name):
self.name = name
self.owner = owner
self.owner.fields[self.name] = self
def __get__(self, instance, instance_type):
return self
def __set__(self, instance, value):
self = value
class SuperTable:
fields = {}
field_1=FieldThatKnowsItsName()
field_2=FieldThatKnowsItsName()
table = SuperTable()
print(table.field_1.name)
print(table.field_2.name)
You can then add methods and or extend your datatype as you like.
As a bonus, the set_name(self, owner, name) dunder also passes the parent instance, so the Field class instance can register itself with the parent.
I got this from Effective Python by Brett Slatkin. It took a while to figure out how to implement.
How can I do the same for a variable? As opposed to functions, Python variables do not have the __name__ attribute.
The problem comes up because you are confused about terminology, semantics or both.
"variables" don't belong in the same category as "functions". A "variable" is not a thing that takes up space in memory while the code is running. It is just a name that exists in your source code - so that when you're writing the code, you can explain which thing you're talking about. Python uses names in the source code to refer to (i.e., give a name to) values. (In many languages, a variable is more like a name for a specific location in memory where the value will be stored. But Python's names actually name the thing in question.)
In Python, a function is a value. (In some languages, this is not the case; although there are bytes of memory used to represent the actual executable code, it isn't a discrete chunk of memory that your program logic gets to interact with directly.) In Python, every value is an object, meaning that you can assign names to it freely, pass it as an argument, return it from a function, etc. (In many languages, this is not the case.) Objects in Python have attributes, which are the things you access using the . syntax. Functions in Python have a __name__ attribute, which is assigned when the function is created. Specifically, when a def statement is executed (in most languages, creation of a function works quite differently), the name that appears after def is used as a value for the __name__ attribute, and also, independently, as a variable name that will get the function object assigned to it.
But most objects don't have an attribute like that.
In other words, if I have a variable such as:
That's the thing: you don't "have" the variable in the sense that you're thinking of. You have the object that is named by that variable. Anything else depends on the information incidentally being stored in some other object - such as the locals() of the enclosing function. But it would be better to store the information yourself. Instead of relying on a variable name to carry information for you, explicitly build the mapping between the string name you want to use for the object, and the object itself.

Pass a list to a class python [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
I have this simple class:
class revs:
def __init__(self, rev, us, accs = []):
self.rev = rev
self.us = us
self.accs = accs
And i have this piece of code to asign values to the list and is inside of a loop
rev, usu = cada_l.split("|")
acct = each_l[:2].strip()
list_acct.append(acct)
and last, i create a dict, to manage a list of revs like this:
drevs = {}
cada = revs(rev, us, list_acct)
drevs[cada.rev] = cada
And it Works correctly with rev and us, but with list_acct is ever updating all the instances:
drevs['1'].rev
'1'
drevs['2'].rev
'2'
drevs['1'].us
'user1'
drevs['2'].us
'user2'
drevs['1'].accs
'["Doc1","Doc2"]'
drevs['2'].accs
'["Doc1","Doc2"]'
And if i change list_acct.clear(), the values in all the instances is clear, I'm still fairly new to Python and this confuses me.
Thanks
This looks like it's happening because you're passing the same list to every object. As a result, all the objects maintain references to the same list, and since list is mutable, it appears to change "all" of them at once.
To fix this, either pass in a new empty list each time you create a revs object, or else clone the list you're passing in:
cada = revs(rev, us, list_acct[:])
Note that if list_acct contains mutable objects, you could still get into the same problem again, but one level deeper!
If you're not passing lists to the revs objects when you create them at all (I can't tell, since you're not showing your full code!), then you have the same problem, but for a different reason: in Python, default arguments are all evaluated once, at the time of the function definition. Therefore, you can get this behavior:
r1 = revs(1, 1)
r2 = revs(2, 2)
r1.accs.append("Hi!")
print(r1.accs) # prints ['Hi!']
print(r2.accs) # prints ['Hi!']
Because the default argument for the revs constructor is always pointing to the same list. See this question for an explanation as to why, but to get around it, just use None as your default instead of [].
class revs:
def __init__(self, rev, us, accs=None):
self.rev = rev
self.us = us
if accs is None:
accs = []
self.accs = accs

How do I downcast in python

I have two classes - one which inherits from the other. I want to know how to cast to (or create a new variable of) the sub class. I have searched around a bit and mostly 'downcasting' like this seems to be frowned upon, and there are some slightly dodgy workarounds like setting instance.class - though this doesn't seem like a nice way to go.
eg.
http://www.gossamer-threads.com/lists/python/python/871571
http://code.activestate.com/lists/python-list/311043/
sub question - is downcasting really that bad? If so why?
I have simplified code example below - basically i have some code that creates a Peak object after having done some analysis of x, y data. outside this code I know that the data is 'PSD' data power spectral density - so it has some extra attributes. How do i down cast from Peak, to Psd_Peak?
"""
Two classes
"""
import numpy as np
class Peak(object) :
"""
Object for holding information about a peak
"""
def __init__(self,
index,
xlowerbound = None,
xupperbound = None,
xvalue= None,
yvalue= None
):
self.index = index # peak index is index of x and y value in psd_array
self.xlowerbound = xlowerbound
self.xupperbound = xupperbound
self.xvalue = xvalue
self.yvalue = yvalue
class Psd_Peak(Peak) :
"""
Object for holding information about a peak in psd spectrum
Holds a few other values over and above the Peak object.
"""
def __init__(self,
index,
xlowerbound = None,
xupperbound = None,
xvalue= None,
yvalue= None,
depth = None,
ampest = None
):
super(Psd_Peak, self).__init__(index,
xlowerbound,
xupperbound,
xvalue,
yvalue)
self.depth = depth
self.ampest = ampest
self.depthresidual = None
self.depthrsquared = None
def peakfind(xdata,ydata) :
'''
Does some stuff.... returns a peak.
'''
return Peak(1,
0,
1,
.5,
10)
# Find a peak in the data.
p = peakfind(np.random.rand(10),np.random.rand(10))
# Actually the data i used was PSD -
# so I want to add some more values tot he object
p_psd = ????????????
edit
Thanks for the contributions.... I'm afraid I was feeling rather downcast(geddit?) since the answers thus far seem to suggest I spend time hard coding converters from one class type to another. I have come up with a more automatic way of doing this - basically looping through the attributes of the class and transfering them one to another. how does this smell to people - is it a reasonable thing to do - or does it spell trouble ahead?
def downcast_convert(ancestor, descendent):
"""
automatic downcast conversion.....
(NOTE - not type-safe -
if ancestor isn't a super class of descendent, it may well break)
"""
for name, value in vars(ancestor).iteritems():
#print "setting descendent", name, ": ", value, "ancestor", name
setattr(descendent, name, value)
return descendent
You don't actually "cast" objects in Python. Instead you generally convert them -- take the old object, create a new one, throw the old one away. For this to work, the class of the new object must be designed to take an instance of the old object in its __init__ method and do the appropriate thing (sometimes, if a class can accept more than one kind of object when creating it, it will have alternate constructors for that purpose).
You can indeed change the class of an instance by pointing its __class__ attribute to a different class, but that class may not work properly with the instance. Furthermore, this practice is IMHO a "smell" indicating that you should probably be taking a different approach.
In practice, you almost never need to worry about types in Python. (With obvious exceptions: for example, trying to add two objects. Even in such cases, the checks are as broad as possible; here, Python would check for a numeric type, or a type that can be converted to a number, rather than a specific type.) Thus it rarely matters what the actual class of an object is, as long as it has the attributes and methods that whatever code is using it needs.
See following example. Also, be sure to obey the LSP (Liskov Substitution Principle)
class ToBeCastedObj:
def __init__(self, *args, **kwargs):
pass # whatever you want to state
# original methods
# ...
class CastedObj(ToBeCastedObj):
def __init__(self, *args, **kwargs):
pass # whatever you want to state
#classmethod
def cast(cls, to_be_casted_obj):
casted_obj = cls()
casted_obj.__dict__ = to_be_casted_obj.__dict__
return casted_obj
# new methods you want to add
# ...
This isn't a downcasting problem (IMHO). peekfind() creates a Peak object - it can't be downcast because its not a Psd_Peak object - and later you want to create a Psd_Peak object from it. In something like C++, you'd likely rely on the default copy constructor - but that's not going to work, even in C++, because your Psd_Peak class requires more parameters in its constructor. In any case, python doesn't have a copy constructor, so you end up with the rather verbose (fred=fred, jane=jane) stuff.
A good solution may be to create an object factory and pass the type of Peak object you want to peekfind() and let it create the right one for you.
def peak_factory(peak_type, index, *args, **kw):
"""Create Peak objects
peak_type Type of peak object wanted
(you could list types)
index index
(you could list params for the various types)
"""
# optionally sanity check parameters here
# create object of desired type and return
return peak_type(index, *args, **kw)
def peakfind(peak_type, xdata, ydata, **kw) :
# do some stuff...
return peak_factory(peak_type,
1,
0,
1,
.5,
10,
**kw)
# Find a peak in the data.
p = peakfind(Psd_Peak, np.random.rand(10), np.random.rand(10), depth=111, ampest=222)

How can I define a class in Python?

Quite simple, I'm learning Python, and I can't find a reference that tells me how to write the following:
public class Team {
private String name;
private String logo;
private int members;
public Team(){}
// Getters/setters
}
Later:
Team team = new Team();
team.setName("Oscar");
team.setLogo("http://....");
team.setMembers(10);
That is a class Team with the properties: name/logo/members
Edit
After a few attempts I got this:
class Team:
pass
Later
team = Team()
team.name = "Oscar"
team.logo = "http://..."
team.members = 10
Is this the Python way? It feels odd (coming from a strongly typed language of course).
Here is what I would recommend:
class Team(object):
def __init__(self, name=None, logo=None, members=0):
self.name = name
self.logo = logo
self.members = members
team = Team("Oscar", "http://...", 10)
team2 = Team()
team2.name = "Fred"
team3 = Team(name="Joe", members=10)
Some notes on this:
I declared that Team inherits from object. This makes Team a "new-style class"; this has been recommended practice in Python since it was introduced in Python 2.2. (In Python 3.0 and above, classes are always "new-style" even if you leave out the (object) notation; but having that notation does no harm and makes the inheritance explicit.) Here's a Stack Overflow discussion of new-style classes.
It's not required, but I made the initializer take optional arguments so that you can initialize the instance on one line, as I did with team and team3. These arguments are named, so you can either provide values as positional parameters (as with team) or you can use the argument= form as I did with team3. When you explicitly specify the name of the arguments, you can specify arguments in any order.
If you needed to have getter and setter functions, perhaps to check something, in Python you can declare special method functions. This is what Martin v. Löwis meant when he said "property descriptors". In Python, it is generally considered good practice to simply assign to member variables, and simply reference them to fetch them, because you can always add in the property descriptors later should you need them. (And if you never need them, then your code is less cluttered and took you less time to write. Bonus!)
Here's a good link about property descriptors: http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
Note: Adam Gomaa's blog seems to have disappeared from the web. Here's a link to a saved copy at archive.org:
https://web.archive.org/web/20160407103752/http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
It doesn't really matter if you specify values as part of the call to Team() or if you poke them into your class instance later. The final class instance you end up with will be identical.
team = Team("Joe", "http://example.com", 1)
team2 = Team()
team2.name = "Joe"
team2.logo = "http://example.com"
team2.members = 1
print(team.__dict__ == team2.__dict__)
The above will print True. (You can easily overload the == operator for Team instances, and make Python do the right thing when you say team == team2, but this doesn't happen by default.)
I left out one thing in the above answer. If you do the optional argument thing on the __init__() function, you need to be careful if you want to provide a "mutable" as an optional argument.
Integers and strings are "immutable". You can never change them in place; what happens instead is Python creates a new object and replaces the one you had before.
Lists and dictionaries are "mutable". You can keep the same object around forever, adding to it and deleting from it.
x = 3 # The name "x" is bound to an integer object with value 3
x += 1 # The name "x" is rebound to a different integer object with value 4
x = [] # The name "x" is bound to an empty list object
x.append(1) # The 1 is appended to the same list x already had
The key thing you need to know: optional arguments are evaluated only once, when the function is compiled. So if you pass a mutable as an optional argument in the __init__() for your class, then each instance of your class shares one mutable object. This is almost never what you want.
class K(object):
def __init__(self, lst=[]):
self.lst = lst
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # also prints "[1]"
k1.lst.append(2)
print(k0.lst) # prints "[1, 2]"
The solution is very simple:
class K(object):
def __init__(self, lst=None):
if lst is None:
self.lst = [] # Bind lst with a new, empty list
else:
self.lst = lst # Bind lst with the provided list
k0 = K()
k1 = K()
k0.lst.append(1)
print(k0.lst) # prints "[1]"
print(k1.lst) # prints "[]"
This business of using a default argument value of None, then testing that the argument passed is None, qualifies as a Python design pattern, or at least an idiom you should master.
class Team:
def __init__(self):
self.name = None
self.logo = None
self.members = 0
In Python, you typically don't write getters and setters, unless you really have a non-trivial implementation for them (at which point you use property descriptors).
To write classes you would normally do:
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
To instantiate instances of a class(es) you would do
person1 = Person("Oscar", 40, "6ft")
person2 = Team("Danny", 12, "5.2ft")
You can also set a default value:
class Person:
def __init__(self):
self.name = "Daphne"
self.age = 20
self.height = "5.4ft"
To instantiate a classes set like this, you want to do:
person3 = Person()
person3.name = "Joe"
person3.age = 23
person3.height = "5.11ft"
You will notice that this method bears a lot of similarity to your typical Python dictionary interaction.

Categories

Resources