How to Add a Method to a Method In a Class? - python

Let's say I have the following class:
import webbrowser
class Management(object):
def add_accounts(self, data):
operations = []
for account_name in data:
operations.append(
{'operator': 'ADD',
'operand': {'name': account_name}
})
self.added = operations
manager = Management()
manager.add_accounts(['name 1', 'name 2'])
What I want to do is add this function:
def source():
url = r'http://www.stackoverflow.com/some-help-doc'
webbrowser.open(url, new=1)
to the add_accounts method so I can type the following:
manager.add_accounts.source()
and have it open my default browser to the help article online:
I've been searching for how to add a method to an already existing method in a class. Is there a name for what I'm trying to do?

As #BrenBam pointed out in comments, methods do have attributes and those can be anything, including functions. However, this'll make for some weird un-Pythonic code. If you want this method to show some kind of documentation (as example suggests), it'll be better to just copypaste information to a docstring. Docstring is where everyone expects info to be.

This seems like the best option for now:
import webbrowser
class Management(object):
def __init__(self):
Management.add_accounts.source = lambda url='https://github.com/': webbrowser.open(url, new=1)
def add_accounts(self, data):
operations = []
for account_name in data:
operations.append(
{'operator': 'ADD',
'operand': {'name': account_name}
})
self.added = operations
manager = Management()

Related

Python How to call a class method without importing

I found this piece of code in this repo. https://github.com/adw0rd/instagrapi/blob/master/instagrapi/mixins/fbsearch.py
And I wonder how self.private_request() could be called because there is no import and private_request() isn't defined in this class.
from instagrapi.extractors import extract_location
class FbSearchMixin:
def fbsearch_places(self, query: str, lat: float = 40.74, lng: float = -73.94):
params = {
'search_surface': 'places_search_page',
'timezone_offset': self.timezone_offset,
'lat': lat,
'lng': lng,
'count': 30,
'query': query,
}
result = self.private_request("fbsearch/places/", params=params)
locations = []
for item in result['items']:
locations.append(extract_location(item['location']))
return locations
It would be glad if someone could explain why and how this is possible.
This code doesn't call fbsearch_places; only defines it. If it called it without defining private_request, that would cause an exception.
But if someone imports this code and uses this class in combination with another class where private_request is defined, then they will be able to use the fbsearch_places method.
That's what "mix in" means in FbSearchMixin: that the class that is supposed to be used in combination with another class in multiple inheritance.

Python: mapping between class and json

I am getting Data via a REST-Interface and I want to store those data in a class-object.
my class could looks like this:
class Foo:
firstname = ''
lastname = ''
street = ''
number = ''
and the json may look like this:
[
{
"fname": "Carl",
"lname": "any name",
"address": ['carls street', 12]
}
]
What's the easiest way to map between the json and my class?
My problem is: I want to have a class with a different structure than the json.
I want the names of the attributes to be more meaningful.
Of course I know that I could simply write a to_json method and a from_json method which does what I want.
The thing is: I have a lot of those classes and I am looking for more declarative way to write the code.
e.g. in Java I probably would use mapstruct.
Thanks for your help!
Use a dict for the json input. Use **kwargs in an __init__ method in your class and map the variables accordingly.
I had a similar problem, and I solved it by using #classmethod
import json
class Robot():
def __init__(self, x, y):
self.type = "new-robot"
self.x = x
self.y = y
#classmethod
def create_robot(cls, sdict):
if sdict["type"] == "new-robot":
position = sdict["position"]
return cls(position['x'], position['y'])
else:
raise Exception ("Unable to create a new robot!!!")
if __name__=='__main__':
input_string = '{"type": "new-robot", "position": {"x": 3, "y": 3}}'
cmd = json.loads(input_string)
bot = Robot.create_robot(cmd)
print(bot.type)
Perhaps you could you two classes, one directly aligned with the Json (your source class) and the other having the actual structure you need. Then you could map them using the ObjectMapper class[https://pypi.org/project/object-mapper/]. This is very close to the MapStruct Library for Java.
ObjectMapper is a class for automatic object mapping. It helps you to create objects between project layers (data layer, service layer, view) in a simple, transparent way.

Django-admin linked column issue

Following this answer I wanted to add a linked column to my admin page,
class AnswerAdmin(admin.ModelAdmin):
list_display = ('__str__', 'link_to_question', 'time_created', 'time_updated', 'created_by', 'down_vote', 'up_vote')
def link_to_question(self, obj):
link = urlresolvers.reverse("admin:QnA_question_change",
args=[obj.question.id]) # model name has to be lowercase
text = obj.question.__str__
str = format_html("{}", text)
return mark_safe(u'%s' % (link, str))
class Meta:
model = Answer
but what I get in return is this:
<bound method Entry.__str__ of <Question: This is a question about technology?>>
I only want the "This is a question ..." part shown in my admin.
Sidenote:
when I use something like obj.question.text instead of a function it works smoothly.
It isn't clear why you are using format_html then passing the result to mark_safe. You should be able to do it in one step with format_html. This has the advantage of escaping text, in case a use has inserted malicious content.
link = urlresolvers.reverse(...)
text = obj.question
link_str = format_html('{}', link, text)
To call the __str__ method you need to call it with obj.question.__str__(). However, it's more pythonic to call str(obj.question) rather than obj.question.__str__(). In this case, I don't think you need to use str() at all, since you are using format_html.
just set the allow_tags = True property of the method.
class AnswerAdmin(admin.ModelAdmin):
list_display = ('__str__', 'link_to_question', 'time_created', 'time_updated', 'created_by', 'down_vote', 'up_vote')
def link_to_question(self, obj):
link = urlresolvers.reverse("admin:QnA_question_change",
args=[obj.question.id]) # model name has to be lowercase
return u'{1}'.format(link, obj.question)
link_to_question.short_description = u'Link'
link_to_question.allow_tags = True

getattr() without 'class object'

How can I use getattr without "Class" per se ?
So I have this situation: I have 'columns' that are asking mysql for specific data in a specific order. data is printed via flask/apache so that user has ability to manipulate this data. Now, From flask, POST methdd, I'm receiving changed(?) values and I am storing them in python attributes.I need to check if values within those attributes are same as in original data. Sure, I could hardcore it but I would like have possibility of change columns dynamically.
columns = ["username", "email", "admin"]
data = ("john", "john#snow.com", "True")
username = "john"
email = "different#email.com"
admin = False
Not sure how can I approach it ?
for i in data:
if i == getattr(???, 'username'):
print("it's the same")
or something like this?:
for i in data:
if i == getattr(data, '?????'):
print("it's the same")
Everything is within flask, I cannot embed it into the Class per se. So I don't have 'self' etc.
If I could create class I would probably make something like
class Myclass:
def __init__(self):
self.columns = ["username", "email", "admin"]
self.data = ("john", "john#snow.com", "True")
self.result = []
self.username = "john"
self.email = "different#email.com"
self.admin = False
def test(self):
for i in self.data:
if i == getattr(self, self.columns[self.data.index(i)]):
self.result.append("same")
else:
self.result.append("different")
return self.result
Myclass().test()
['same', 'different', 'different']
It turned out that I was looking for simple eval(). getattr() is designed for different purposes.
so simple:
for i in data:
if i == eval(cols[data.index(i)]):
print("it's the same")
did the trick
Flask is just Python code. You can create a class and use that if that fits your use-case. Or, if you used Flask-SQLAlchemy to manage database-backed data you'd have classes and instances anyway (and get easier data updates to boot).
And classes and instances are not the only objects with attributes; modules and functions have attributes too (although you wouldn't store your data as attributes on either of those), and when you look up methods on anything, you are looking up attributes too.
Pick a storage, then either wrap that storage with an instance of a class, and use getattr(), or pick a different data structure and use the methods for that data structure to get at the different fields. A dictionary, for instance, would make it trivial to get the current value for a given name.
If you do stick to instances, then note that in your loop you'd want to zip your columns and data values together:
for name, value in zip(columns, data):
if getattr(self, name) == value:
self.result.append("same")
else:
self.result.append("different")
Note that you do not have to add "self." in front, the whole point of getattr() is do the same work the . syntax does.
You probably want to put your columns and data lists together as a dictionary:
self.data = {'username': 'john', 'email': 'john#snow.com', 'admin': 'True'}
because that's how you'd process POST data from a form anyway; that way you can iterate over the dict.items() pairs, or use just the columns list to access values:
for name, value in self.data.items():
# ...
or use dict.get() to retrieve values, allowing for missing entries:
for name in self.columns:
if getattr(self, name) == self.data.get(name):
# ...

Python Inspect - Lookup the data type for a property in a GAE db.model Class

class Employee(db.Model):
firstname = db.StringProperty()
lastname = db.StringProperty()
address1 = db.StringProperty()
timezone = db.FloatProperty() #might be -3.5 (can contain fractions)
class TestClassAttributes(webapp.RequestHandler):
"""
Enumerate attributes of a db.Model class
"""
def get(self):
for item in Employee.properties():
self.response.out.write("<br/>" + item)
#for subitem in item.__dict__:
# self.response.out.write("<br/> --" + subitem)
The above will give me a list of the property names for the variable "item".
My idea of item.__dict__ didn't work because item was a str.
How can I then display the data field type for each property, such as db.FloatProperty() for the property called timezone?
GAE = Google App Engine - but I'm sure the same answer would work for any class.
Thanks,
Neal Walters
Iterate using "for name, property in Employee.properties().items()". The property argument is the Property instance, which you can compare using instanceof.
For problems like these, the interactive Python shell is really handy. If you had used it to poke around at your Employee object, you might have discovered the answer to your question through trial and error.
Something like:
>>> from groups.models import Group
>>> Group.properties()
{'avatar': <google.appengine.ext.db.StringProperty object at 0x19f73b0>,
'created_at': <google.appengine.ext.db.DateTimeProperty object at 0x19f7330>,
'description': <google.appengine.ext.db.TextProperty object at 0x19f7210>,
'group_type': <google.appengine.ext.db.StringProperty object at 0x19f73d0>}
From that you know that the properties() method of a db.Model object returns a dict mapping the model's property names to the actual property objects they represent.
I add the same problem, and the first 2 answers did not help me 100%.
I was not able to get the type information, from the meta data of the class or the
instance property, which is bizarre. So I had to use a dictionary.
The method GetType() will return the type of the property as a string.
Here is my answer:
class RFolder(db.Model):
def GetPropertyTypeInstance(self, pname):
for name, property in self.properties().items():
if name==pname:
return property
return None
def GetType(self, pname):
t = self.GetPropertyTypeInstance(pname)
return RFolder.__DB_PROPERTY_INFO[type(t)]
__DB_PROPERTY_INFO = {
db.StringProperty :"String",
db.ByteStringProperty :"ByteString",
db.BooleanProperty :"Boolean",
db.IntegerProperty :"Integer",
db.FloatProperty :"Float",
db.DateTimeProperty :"DateTime",
db.DateProperty :"Date",
db.TimeProperty :"Time",
db.ListProperty :"List",
db.StringListProperty :"StringList",
db.ReferenceProperty :"Reference",
db.SelfReferenceProperty :"SelfReference",
db.UserProperty :"User",
db.BlobProperty :"Blob",
db.TextProperty :"Text",
db.CategoryProperty :"Category",
db.LinkProperty :"Link",
db.EmailProperty :"Email",
db.GeoPtProperty :"GeoPt",
db.IMProperty :"IM",
db.PhoneNumberProperty :"PhoneNumber",
db.PostalAddressProperty :"PostalAddress",
db.RatingProperty :"Rating"
}

Categories

Resources