How to set a variable outside of a loop in python - python

I'm trying to set a variable outside of the scope of the current loop.
My scenario is this: I have 2 lists. One containing a list of comment objects, and each comment has a reference to a user id. My second list contains all the user objects, based on the users id.
What I am trying to do is iterate through each comment, and then modify the comment object within the list to contain the users name, so that when I pass the list of comments back, it has the name embedded.
So far, this how I am trying to achieve this:
# iterate through the comments and add the display name to the comment obj
for comment in comments:
# Create the user to use later
user = None
# Iterate the comment_users and get the user who matches the current comment.
for comment_user in comment_users:
if comment_user['_id'] is comment['created_by']:
user = comment_user # this is creating a new user in the for comment_user loop
break
print(user)
# get the display name for the user
display_name = user['display_name']
# Add the user display name to the comment
comment.user_display_name = display_name
Now, from what I am starting to understand from Python's scope, is that the user = comment_user line in the second for loop is creating a new user variable within the scope of the second for loop, which is ignoring the user variable defined in the first for loop.
I'm using Python 3, so I thought that the nonlocal keyword would be the way to go, but I'm not sure if that is just for functions or not, as I couldn't get it to work.
So, I was wondering if anyone could provide a way to achieve this? Is there a more 'pythonic' way to achieve this?

I think the problem is your use of is. Try this code:
for comment in comments:
for comment_user in comment_users:
if comment_user['_id'] == comment['created_by']:
comment.user_display_name = comment_user['display_name']
break
This problem occurs when you are (incorrectly) using is to compare string objects. The equality operator (==) checks if the contents of the two strings are the same, whereas is operator actually checks if they are the same object. If the strings are interned, they may give the same result, but generally speaking you should never use is for string comparison.

I think a more pythonic way would be to make comment_user a dictionary that has the _id as key, so that you don't have to loop over the list but can just do
for comment in comments:
comment.user_display_name = comment_user[comment['created_by']]['display_name']

Related

I can't think of a way to aviod dynamic variable in this case

EDIT BELOW
I read a lot of discussion about dynamic variable in python and a lot have shown that they are things that you generally shouldn't do. However I have a case that I really can't think of a way to avoid it (and hence why I am here)
I am currently reading in a text file that stores the information of different members of a store. Each members have their own information: like their phone number, email, points in their accounts, etc. I want to create a class and objects that stores this information. Don't worry this is just a part of an assignment and they are not real people. Here is the sample code:
class Member:
def __init__(self, name, phoneNumber, email, points):
self.name = name
self.phoneNumber = phoneNumber
self.email = email
self.points = points
self.totalPointsSpent = 0
#There are methods below that will return some calculated results, like total points
spent or their ranking. I will not show those here as they are irrelevant
And for each member in the file will be read in and create an object out of it. For example if there are 5 members in that file, five objects will be created, and I want to name them member1, member2, etc. However, this will be confusing when accessing it, as it will be hard to tell them apart, so I add them to a dictionary, with their memberID as the key:
dictonary[memberID] = member1
dictonary[memberID] = member2 #and so on
which would result in a dictionary that look like something like this:
dictionary = {'jk1234':member1,'gh5678':member2,...#etc}
This is the interesting thing about python is the fact that the dictionary value does not need to be a value, it can be an object. This is something new to me, coming from Java.
However, here is the first problem. I do not know how many member are there in a file, or I should say, the number of members in the file varies from file to file. If the number is 5, I need 5 variables; if there are 8, I need 8, and so on. I have thought of using a while loop, and the code will look something like this:
a = len(#the number of members in the file.)
i = 0
while i <= a:
member + i = Member(#pass in the information)
but the '+' operator only works for strings when combining names, not for identifiers. And thus cannot work (I think).
Many solution I read has indicated that I should use a dictionary or a list in such case. However, since my variables are pointing towards a class object, I cannot think of a way to use list/dictionary as the implementation.
My current solution is to use a tuple to store the members information, and do the calculations elsewhere (i.e. I took out the class methods and defined them as functions) so that the dictionary looks like this:
dictionary = {'jk1234': (name, phoneNumber, email, points),'gh5678':(name, phoneNumber, email, points),...#etc}
However given the amount of information I need to pass in it is less than ideal. My current algorithm works, but I want to optimized it and make it more encapsulated.
If you made this far I appreciate it, and would like to know if there is a better algorithm for this problem. Please excuse me for my less than a year of Python experience.
EDIT: Okay I just discovered something really interesting that might seems basic to experienced Python programmers. That is you can pass the values inside the dictionary, instead of naming a variable exclusively. For my problem, that would be:
dictionary = {'jk1234': Member(name, phoneNumber, email, points),'gh5678':Member(name, phoneNumber, email, points),...#etc}
#and they are retrievable:
dictionary['jk1234'].get_score() #get_score is a getter function inside the class
And it would return the proper value. This seems to be a good solution. However, I would still love to hear other ways to think about this problem
It looks like you are on the right track with your update.
It's not clear to me if your MemberID is a value for each member, but if it is, you can use it in a loop creating the objects
d = {}
for member in members_file:
d[MemberID] = Member(name, phoneNumber, email, points)
This, of course, assumes that MemberID is unique, otherwise you will overwrite existing entries.
You could also use a list instead
l = []
for member in members_file:
l.append(Member(name, phoneNumber, email, points))
Further, you could also do the above with list/dict comprehensions which are basically just condensed for-loops.
Dict-comprehension
d = {MemberID: Member(name, phoneNumber, email, points) for member in members_file}
List-comprehension
l = [Member(name, phoneNumber, email, points) for member in members_file]
Whether a list or a dictionary makes more sense, is up to your use-case.
Also note that the above assumes various things, e.g. on what form you get the data from the file, since you did not provide that information.
You can create a dictionary with a for loop:
d = {}
for i in the_members_in_the_file:
d[i] = Member(your, parameters, that\'s, inputted)
You will get your expected dictionary.

Python when a list isn't a list? list object has no attribute split

I have a list of user ids being sent to my API:
Users = ['x1','x2']
In my serializer create method I try to iterate over them:
users = validated_data.get('users', None)
for user in users:
print(user)
print("===")
The output I receive is:
> x1,x2
> ===
It instead of iterating over the list, it outputs as one line! This suggests to me it is not a list, but a string, so I checked the type(users) which gave a blank i.e. no type.
So I tried splitting users up users.split() Then I get a contradiction" "list object has no attribute split"!
What is wrong here, confused?
It seems that users is a list that looks like this: ['x1,x2']. Instead of what you expected: ['x1','x2']
You can use ast.literal_eval to make a list first, or you can just split that one element:
users = ['x1,x2']
for user in users[0].split(','):
print(user)
print('===')
Output:
>>> users = ['x1,x2']
>>> for user in users[0].split(','):
... print(user)
... print('===')
x1
===
x2
===
A per your comment:
from ast import literal_eval
users = ['[1,2]']
users = literal_eval(users[0])
for user in users:
print(user)
print('===')
Output:
1
===
2
===
For users = ['[x1,x1]']:
users = ['[x1,x1]']
users = users[0][1:-1]
for user in users.split(','):
print(user)
print('===')
Others have pointed out that you seem to have a list containing a single string, ['x1, x2'].
I took a look at the Django REST Framework internals, and it's definitely returning a list.
ListField inherits from Field, which defines a few methods, including run_validation, to_internal_value, and to_representation.
Two of those methods are abstract, and one of them, run_validation actually invokes the validation by calling self.to_internal_value(data).
So to see what the validator is doing, we have to look at the ListField's implementation of to_internal_value.
The comment inside of to_internal_value says this:
"""
List of dicts of native values <- List of dicts of primitive datatypes.
"""
Then it checks for invalid input types and finally, calls run_validation.
According to my IDE, there are 5 implementations of run_validation in Django REST Framework. The most relevant one is probably ListSerializer.
The comments above ListSerializer tell us that we're probably in the right place:
# There's some replication of `ListField` here,
# but that's probably better than obfuscating the call hierarchy.
The ListSerializer class validates each item (source) and then appends it to a list called ret. So we should be returning a list.
The unsolved piece of the puzzle here is what your input is that is causing the output to be incorrect, but tracing through the call stack, the code appears to be working as intended.
EDIT:
Could it be that to_representation is flattening your list because it thinks it's a dictionary?
It looks as is users is a list with a single item x1,x2:
>>> users = ['x1,x2']
>>> for user in users:
... print(user)
... print("===")
...
x1,x2
===

Overwriting an entity by reusing its id

If we add a second entity of same Model(NDB) with the same id, would the first entity get replaced by the second entity?
Is this the right way? In future, would this cause any problem?
I use GAE Python with NDB.
Eg,
class X (ndb.Model):
command = ndb.StringProperty ()
x_record = X (id="id_value", command="c1")
x_record.put ()
# After some time
x_record = X (id="id_value", command="c2")
x_record.put ()
I did find a mention of this in official Google docs.
CONTEXT
I intend to use it to reduce code steps. Presently, first the code checks if an entity with key X already exists. If it exists, it updates its properties. Else, it creates a new one with that key(X). New approach would be to just blindly create a new entity with key X.
Yes, you would simply replace the model.
Would it cause any problems? Only if you wanted the original model back...

How to check the existance of single Entity? Google App Engine, Python

Sorry for noobster question again.
But I'm trying to do some very easy stuff here, and I don't know how. Documentation gives me hints which do not work, or apply.
I recieve a POST request and grab a variable out of it. It says "name".
I have to search all over my entities Object (for example) and find out if there's one that has the same name. Is there's none, I must create a new Entity with this name. Easy it may look, but I keep Failing.
Would really appreciate any help.
My code currently is this one:
objects_qry = Object.query(Object.name == data["name"])
if (not objects_qry ):
obj = Object()
obj .name = data["name"]
obj .put()
class Object(ndb.Model):
name = ndb.StringProperty()
Using a query to perform this operation is really inefficient.
In addition your code is possibly unreliable, if name doesn't exist and you have two requests at the same time for name you could end up with two records. And you can't tell because your query only returns the first entity with the name property equal to some value.
Because you expect only one entity for name a query is expensive and inefficient.
So you have two choices you can use get_or_insert or just do a get, and if you have now value create a new entity.
Any way here is a couple of code samples using the name as part of the key.
name = data['name']
entity = Object.get_or_insert(name)
or
entity = Object.get_by_id(name)
if not entity:
entity = Object(id=name)
entity.put()
Calling .query just creates a query object, it doesn't execute it, so trying to evaluate is as a boolean is wrong. Query object have methods, fetch and get that, respectively, return a list of matching entities, or just one entity.
So your code could be re-written:
objects_qry = Object.query(Object.name == data["name"])
existing_object = objects_qry.get()
if not existing_object:
obj = Object()
obj.name = data["name"]
obj.put()
That said, Tim's point in the comments about using the ID instead of a property makes sense if you really care about names being unique - the code above wouldn't stop two simultaneous requests from creating entities with the same name.

Limiting one Content item per Member in a Folder on Plone 4

I have created a custom Archetypes content type called "Résumé" and would like to enforce a limitation that lets a Member add only one item of this type inside a folder. Even better would be redirecting the member to the edit page of his or her item, if it already exists in that folder.
How can I enforce this limitation and provide this extra functionality?
A solution to a similar usecase for Plone 3 can be found in the eestec.base. We did it by overriding the createObject.cpy and adding a special check for this.
Code is in the collective SVN, at http://dev.plone.org/collective/browser/eestec.base/trunk/eestec/base/skins/eestec_base_templates/createObject.cpy, lines 20-32.
Well, it is a sort of validation constraint, so maybe add a validator to the title field that in reality does not bother about the title, but checks the user etc.? (I think a field validator is passed enough information to pull this off, if not, overriding the post_validate method or listening to the corresponding event should work.)
If you try this, bear in mind that post_validate is already called while the user is editing (ie on moving focus out of a field).
I dont't know if it's best practice, but you can hook up on def at_post_create_script from base object on creation and manage_beforeDelete on delete.
for example:
from Products.ATContentTypes.lib import constraintypes
class YourContentype(folder.ATFolder)
[...]
def at_post_create(self):
origin = aq_parent(aq_inner(self))
origin.setConstrainTypesMode(constraintypes.ENABLED) # enable constrain types
org_types = origin.getLocallyAllowedTypes() # returns an immutable tuple
new_types = [x for x in org_types if x! = self.portal_type] # filter tuple into a list
origin.setLocallyAllowedTypes(new_types)
def manage_beforeDelete(self, item, container)
BaseObject.manage_beforeDelete(self, item, container) # from baseObject
self._v_cp_refs = None # from baseObject
origin = aq_parent(aq_inner(self))
origin.setConstrainTypesMode(constraintypes.ENABLED) # enable constrain types
org_types = origin.getLocallyAllowedTypes() # returns an immutable tuple
new_types = [x for x in org_types].append(self.portal_type)
origin.setLocallyAllowedTypes(new_types)
Note: There is also a method called setImmediatelyAddableTypes(), which you may want to explore.
Note II: This does not survive content migration.

Categories