Have a look at the python documentation for filter(function, iterable) (from here):
Construct an iterator from those elements of iterable for which function returns true.
So in order to get a list back you have to use list class:
shesaid = list(filter(greetings(), ["hello", "goodbye"]))
But this probably isn't what you wanted, because it tries to call the result of greetings(), which is "hello", on the values of your input list, and this won't work. Here also the iterator type comes into play, because the results aren't generated until you use them (for example by calling list() on it). So at first you won't get an error, but when you try to do something with shesaid it will stop working:
>>> print(list(shesaid))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
If you want to check which elements in your list are equal to "hello" you have to use something like this:
shesaid = list(filter(lambda x: x == "hello", ["hello", "goodbye"]))
(I put your function into a lambda, see Randy C's answer for a "normal" function)
Answer from TobiMarg on Stack OverflowHave a look at the python documentation for filter(function, iterable) (from here):
Construct an iterator from those elements of iterable for which function returns true.
So in order to get a list back you have to use list class:
shesaid = list(filter(greetings(), ["hello", "goodbye"]))
But this probably isn't what you wanted, because it tries to call the result of greetings(), which is "hello", on the values of your input list, and this won't work. Here also the iterator type comes into play, because the results aren't generated until you use them (for example by calling list() on it). So at first you won't get an error, but when you try to do something with shesaid it will stop working:
>>> print(list(shesaid))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
If you want to check which elements in your list are equal to "hello" you have to use something like this:
shesaid = list(filter(lambda x: x == "hello", ["hello", "goodbye"]))
(I put your function into a lambda, see Randy C's answer for a "normal" function)
filter expects to get a function and something that it can iterate over. The function should return True or False for each element in the iterable. In your particular example, what you're looking to do is something like the following:
In [47]: def greetings(x):
....: return x == "hello"
....:
In [48]: filter(greetings, ["hello", "goodbye"])
Out[48]: ['hello']
Note that in Python 3, it may be necessary to use list(filter(greetings, ["hello", "goodbye"])) to get this same result.
Videos
The OP wants to do this MyData.objects.filter(id>1).
Let's face it.
The problem is Python is greedy (eagerly evaluates expressions), not lazy like Haskell.
Watch David Beazley - Lambda Calculus from the Ground Up - PyCon 2019 for mind-bending λ thing.
Python evaluates id > 1 before calling filter. If we can stop the evaluation for now, we can pass the expression unevaluated to the filter function.
But we can delay expression evaluation until required if we enclose the expression in a function. That's the idea.
The function interface would be filter(lambda: id > 1) if we could implement it.
This interface will be super versatile because any Python expression can be passed and abused.
The implementation;
if we invoke the lambda or any other function with the expression id > 1, Python looks up the name id in the local, enclosing, global scope or builtins depending on the context where the function is invoked.
If we can introduce an object with the name id somewhere in the look-up path before Python finds id in the builtins we can redefine the semantics of the expression.
I'm gonna do it with eval which evaluates expressions in the given context.
CopyDATA = [
{'id': 1, 'name': 'brad', 'color':'red'},
{'id': 2, 'name': 'sylvia', 'color':'blue'},
]
def myfilter(a_lambda):
return filter(lambda obj: eval(a_lambda.__code__, obj.copy()),
DATA)
I pass a dict.copy to eval because eval modifies it's globals object.
See it in action in the context of Model class
CopyIn [1]: class Data(Model):
...: name = str()
...: id = int()
...: color = str()
...:
In [2]: Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})
In [3]: Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})
In [4]: Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})
In [5]: Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})
In [6]: Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})
In [7]: Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})
In [8]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])
[{'color': 'red', 'id': 1, 'name': 'brad'}]
In [9]: pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [10]: pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])
[{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [11]: pprint([vars(obj) for obj in Data.objects.filter(lambda: "e" in color and (name is "brad" or name is "sylvia"))])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [12]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'red', 'id': 3, 'name': 'paul'},
{'color': 'green', 'id': 5, 'name': 'martin'}]
The Data class inherits from Model. The Model gives Data the __init__ method and a class attribute named objects that points to a MetaManager instance which is a descriptor.
The MetaManager returns a Manager instance to sub classes of Model upon access of objects attribute from the subclass. The MetaManger identifies the accessing class and passes that to the Manager instance.
The Manager handles object creation, persistence and fetch.
The db is implemented as a class attribute of Manager for simplicity.
To stop abuse with global objects via functions the filter function raises an exception if a lambda is not passed.
Copyfrom collections import defaultdict
from collections.abc import Callable
class MetaManager:
def __get__(self, obj, objtype):
if obj is None:
return Manager(objtype)
else:
raise AttributeError(
"Manger isn't accessible via {} instances".format(objtype)
)
class Manager:
_store = defaultdict(list)
def __init__(self, client):
self._client = client
self._client_name = "{}.{}".format(client.__module__, client.__qualname__)
def create(self, **kwargs):
self._store[self._client_name].append(self._client(**kwargs))
def all(self):
return (obj for obj in self._store[self._client_name])
def filter(self, a_lambda):
if a_lambda.__code__.co_name != "<lambda>":
raise ValueError("a lambda required")
return (
obj
for obj in self._store[self._client_name]
if eval(a_lambda.__code__, vars(obj).copy())
)
class Model:
objects = MetaManager()
def __init__(self, **kwargs):
if type(self) is Model:
raise NotImplementedError
class_attrs = self.__get_class_attributes(type(self))
self.__init_instance(class_attrs, kwargs)
def __get_class_attributes(self, cls):
attrs = vars(cls)
if "objects" in attrs:
raise AttributeError(
'class {} has an attribute named "objects" of type "{}"'.format(
type(self), type(attrs["objects"])
)
)
attrs = {
attr: obj
for attr, obj in vars(cls).items()
if not attr.startswith("_") and not isinstance(obj, Callable)
}
return attrs
def __init_instance(self, attrs, kwargs_dict):
for key, item in kwargs_dict.items():
if key not in attrs:
raise TypeError('Got an unexpected key word argument "{}"'.format(key))
if isinstance(item, type(attrs[key])):
setattr(self, key, item)
else:
raise TypeError(
"Expected type {}, got {}".format(type(attrs[key]), type(item))
)
if __name__ == "__main__":
from pprint import pprint
class Data(Model):
name = str()
id = int()
color = str()
Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})
Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})
Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})
Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})
Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})
Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})
pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])
pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])
pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])
pprint(
[
vars(obj)
for obj in Data.objects.filter(
lambda: "e" in color and (name is "brad" or name is "sylvia")
)
]
)
pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])
If you want the full django Model experience, i.e.:
- create a new feature vector or data entry with
datapoint = MyData(name='johndoe', color='green', ...)just like in django: e.g.new_user=User(username='johndoe', email='jd@jd.com'); - use the
MyData.objectsfor object management, likeMyData.objects.filter(color__eq='yellow');
here is an approach on how the logic could look like.
First you need basically a naive ObjectManager class:
Copyimport collections
import operator
import inspect
class ObjectManager(collections.MutableSet):
def __init__(self):
# this will hold a list of all attributes from your custom class, once
# initiated
self._object_attributes = None
self._theset = set()
def add(self, item):
self._theset.add(item)
def discard(self, item):
self._theset.discard(item)
def __iter__(self):
return iter(self._theset)
def __len__(self):
return len(self._theset)
def __contains__(self, item):
try:
return item in self._theset
except AttributeError:
return False
def set_attributes(self, an_object):
self._object_attributes = [
a[0] for a in inspect.getmembers(
an_object, lambda a:not(inspect.isroutine(a))
) if not(a[0].startswith('__') and a[0].endswith('__'))
]
def filter(self, **kwargs):
"""Filters your objects according to one or several conditions
If several filtering conditions are present you can set the
combination mode to either 'and' or 'or'.
"""
mode = kwargs.pop('mode', 'or')
ok_objects = set()
for kw in kwargs:
if '__' in kw:
_kw, op = kw.split('__')
# only allow valid operators
assert op in ('lt', 'le', 'eq', 'ne', 'ge', 'gt')
else:
op = 'eq'
_kw = kw
_oper = getattr(operator, op)
# only allow access to valid object attributes
assert _kw in self._object_attributes
n_objects = (
obj for obj in self
if _oper(getattr(obj, _kw), kwargs[kw])
)
if mode == 'and':
if n_objects:
ok_objects = ok_objects.intersection(n_objects)\
if ok_objects else set(n_objects)
else:
return set()
else:
ok_objects.update(n_objects)
return ok_objects
# feel free to add a `get_or_create`, `create`, etc.
Now you attach an instance of this class as attribute to your MyData class and make sure all new objects are added to it:
Copyclass MyData:
# initiate the object manager
objects = ObjectManager()
def __init__(self, uid, name, color):
self.uid = uid
self.name = name
self.color = color
# populate the list of query-able attributes on creation
# of the first instance
if not len(self.objects):
self.objects.set_attributes(self)
# add any new instance to the object manager
self.objects.add(self)
Now you can import your feature vector:
CopyDATA = [
{'uid': 1, 'name': 'brad', 'color':'red'},
{'uid': 2, 'name': 'sylvia', 'color':'blue'},
]
for dat in DATA:
myData(**dat)
or create new instances:
Copyd1 = MyData(uid=10, name='john', color='yellow')
and make use of the manager to filter your objects:
Copyprint([md.name for md in MyData.objects.filter(uid__ge=10)])
# > ['john']
print([md.name for md in MyData.objects.filter(mode='and',uid__ge=1,name__eq='john')])
# > ['john']
print([md.name for md in MyData.objects.filter(mode='or',uid__le=4,name__eq='john')])
# > ['john', 'brad', 'sylvia']
If you cannot or don't want to change the class you want an object manager for, and you are willing to monkey patch around (note that I'm not advertising this!) you can even create a ObjectManager that can be hooked to an arbitrary class (built-in types won't work though) after definition or even initiation of some instances.
The idea is to monkey patch __init__ of the target class and add the objects attribute upon init of an instance of your ObjectManager:
Copyimport gc
import inspect
import collections
import operator
import wrapt # not standard lib > pip install wrapt
class ObjectManager(collections.MutableSet):
def __init__(self, attach_to):
self._object_attributes = None
# add self as class attribute
attach_to.objects = self
# monkey patch __init__ of your target class
@wrapt.patch_function_wrapper(attach_to, '__init__')
def n_init(wrapped, instance, args, kwargs):
wrapped(*args, **kwargs)
c_objects = instance.__class__.objects
if not c_objects:
c_objects.set_attributes(instance)
c_objects.add(instance)
# make sure to be up to date with the existing instances
self._theset = set(obj for obj in gc.get_objects() if isinstance(obj, attach_to))
# already fetch the attributes if instances exist
if self._theset:
self.set_attributes(next(iter(self._theset)))
...
# the rest is identical to the version above
So now this is how you would use it:
Copyclass MyData:
def __init__(self, uid, name, color):
self.uid = uid
self.name = name
self.color = color
# create some instances
DATA = [
{'uid': 1, 'name': 'brad', 'color':'red'},
{'uid': 2, 'name': 'sylvia', 'color':'blue'},
]
my_datas = []
for dat in DATA:
my_datas.append(myData(**dat)) # appending them just to have a reference
# say that ONLY NOW you decide you want to use an object manager
# Simply do:
ObjectManager(MyData)
# and you are done:
print([md.name for md in MyData.objects.filter(mode='or',uid__le=4,name__eq='john')])
# > ['brad', 'sylvia']
# also any object you create from now on is included:
d1 = MyData(uid=10, name='john', color='yellow')
print([md.name for md in MyData.objects.filter(mode='or',uid__le=4,name__eq='john')])
# > ['brad', 'sylvia', 'john']
Like ok... the title said it all. For example : I have a list filled by class name Food. The class uniqueness are defined by name and price attribute. And I create a method to check if the Food is free. the question is how do I use the method to filter the list of Food class using the free checking method of food class?
@dataclass
class Food:
name : str
price : int
def isfree(self):
return self.price == 0
food_list = [Food(name = "curry", price = 5), Food(name = "sushi", price = 0)]
filtered_list = filter(Food.isfree, food_list) # got error TypeError: isfree() takes 0 positional arguments but 2 were given