1

Here I am pulling json data from different websites and I intend to embed some of the retrived information into instance variables. The trouble is, the json package retrieved keeps the info I want under different keys and list positions, and so each dictionary address is unique per website.

I am struggling to find a way of creating a new instance of a class and passing different dictionary keys to lookup for when new data is retrived.

Something like this would be too easy I feel...

import requests, json

class B(object):

    def __init__(self, name, url, size, price):

        self.name = name
        self.url = url

        self.size_address = size
        self.price_address = price

        self.size = 0
        self.price = 0

        self.data = {}

    def retrieve(self):
        #Data grab from web
        try:
            grab = requests.get(self.url, timeout=10)
            self.data = tick.json()
        except:
            raise RuntimeError(self.name + 'Error')


    def size(self):
        self.size = data[self.size_address]
        print self.size

    def price(self):
        self.price = data[self.price_address]
        print self.price

>>> a = B('Dave','www.dave.com/api',['names'][0]['size'],[['names'][0]['prices'][0])
>>> a.size()
42030.20

I've had a look at abstract methods as well as binding functions written outside of the class definition. Binding functions looked to be promising but I couldn't create the same function for each class with different variables because I would be using the same name.

Hopefully someone can point me in the right direction.

Sean Daley
  • 13
  • 3
  • While the websites may use different structures, I imagine the structure for all requests made to a specific website would be consistent (otherwise what's the point of the API). Why not create a dictionary of translation functions (or references to translation functions)? Key the translations off the domain name. – paidhima May 23 '15 at 00:20
  • Along the lines of what @paidhima suggests — if you had a structure describing where the items of interest were located for each website — you could make `size` and `price` [properties](https://docs.python.org/2/library/functions.html?highlight=property#property) of the class that used the website to look-up were/how to get these items and then do it. – martineau May 23 '15 at 07:07

1 Answers1

0

One way to address this is through design-pattern decorators (thanks to martineau for the correction) employing method injection.

Suppose you start off with the following class:

class Info(object):
    def __init__(self, data):
        self.data = data

This class takes a dictionary data, and stores it. The problem is, the key might change among instances.

E.g.,

a1 = Info({'a': 3, 'b': 4})
a2 = Info({'c': 5, 'b': 4})

and we then realize that for a1, the key is a, but for a2, the key is c.

So, we can write a function like so:

import types

def patch_it(info, key):
    def get_it(info):
        return info.data[key]

    info.get_it = types.MethodType(get_it, info)

which takes an Info object and a key, and injects into it a method get_it that returns, when called the dictionary's value of that key.

Here's an example:

>> a1 = Info({'a': 3, 'b': 4})
>> patch_it(a1, 'a')
>> a1.get_it()
3

>> a2 = Info({'c': 5, 'b': 4})
>> patch_it(a2, 'c')
>> a2.get_it()
5

Note

For the specific above example, it is an overkill - Why not add a member to info specifying what is the key? The point is that this method allows you to manipulate objects of Info, in arbitrarily complex ways, independently from the class itself.

Community
  • 1
  • 1
Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
  • What role do decorators have in this? – martineau May 23 '15 at 01:07
  • @martineau The design pattern [decorator](http://en.wikipedia.org/wiki/Decorator_pattern) rather than the Python language feature: "In object-oriented programming, the decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class." – Ami Tavory May 23 '15 at 05:29
  • Yes I had crossed this and thought I had found my solution, but the problem with method injection is that I would like all of the instances to have the method that grabs the size / price out of the dictionary have the same name. So I guess I would need to redefine 'def price' and 'def size' before binding the method. Could I possible create a script where I redifine the two methods after each binding? Thanks a lot for your help guys EDIT: I just realised I need to play with your code a little for it to make sense to me. Let me get back to you :D – Sean Daley May 23 '15 at 08:42
  • @martineau You're right, I agree. In addition, I neglected to thank you for your justified question which led to the link correction. Thanks. – Ami Tavory May 23 '15 at 08:47
  • Ok so I see what you're getting at, but I will need to use a combination of keys, list points to get the exact data I'm looking for. For example I need to retrieve the number 2 from this dict: a1 = Info({'a':{'b':[1,2,3]},'c':5}) – Sean Daley May 23 '15 at 09:02
  • @Sean Daley it's the same principle, though, no? The decorators could get more complicated, or there could be a cascade of decorators, but, personally, I'd go this way. – Ami Tavory May 23 '15 at 09:06
  • I should just say, thanks a lot for your time Ami, this is really helping a lot. This is working, though what would you recommend be the best way to use an arbitrary amount of values? e.g. sometimes the data is in ['size'] and sometimes it's in ['return'][0]['properties']['size'][2]. Should I just make a bunch of if statements to check how many arguments I have and run the appropriate patch function? Or I could just pass all the key values into an array and pass that in. Would still require a few if-thens though. – Sean Daley May 23 '15 at 09:30