temo temo - 3 months ago 12
Python Question

Check if object attribute name appears in a string python

I want to:


  • check whether a string contains an object property

  • if it does then access the attribute



So for an object of class

class Person(object):
name = ""
age = 0
major = ""

def __init__(self, name="", surname="", father="", age =0):
self.name = name
self.surname = surname
self.father = father
self.age = age
self.identity = name +" "+ surname
def __str__(self):
return self.identity

__repr__ = __str__


and object

person = Person("Earl", "Martin", "Jason", 40)


I would like for string "What is the name"
to return person.name
(I already know which object the string is about)

The most basic solution would be to do cases for each property being there but the actual code has quite a few and I am sure I don't manually have to write them out, I am just new to programming so I am not sure what syntax is used for this

Any help appreciated

Answer

As others have noted, getattr is generally useful.

hasattr is of lesser utility; internally, it's basically a getattr call in a try/except AttributeError: block (if AttributeError occurs, it returns False, no exception means True), so if you're considering code like:

if hasattr(myobj, attrname):
    attr = getattr(myobj, attrname)
    ...

just use:

try:
    attr = getattr(myobj, attrname)
except AttributeError:
    pass
else:
    ...

to avoid doubling the number of LEGB lookups, function calls and attribute lookups.

Alternatively, for repeatedly pulling named attribute(s), operator.attrgetter basically lets you make an optimized version of getattr that pre-binds the attribute name to lookup (making it ideal for use with stuff like the map and filter functions, as it makes them more efficient than their equivalent listcomps/genexprs).

On top of those, depending on what your goal is, the dir and (slightly less reliably, due to issues with classes that use __slots__ to define a known set of variables to reduce memory usage and prevent auto-vivification) vars functions may be useful.

For example, in your example case of pulling any attributes corresponding to a word from a string, you could do a bulk identification of legal attribute names using vars()/dir() and your choice of filter or set operations (or a mix) depending on the importance of order, uniqueness, etc.:

from future_builtins import filter  # Only on Py2, not Py3
import operator
import re

def query_obj(obj, querystr):
    # Extract list of legal attribute names from string
    words = re.findall(r'\w+', querystr)

    # Reduce to names present on object's __dict__; no need to construct temporaries
    attrnames = filter(vars(obj).__contains__, words)
    # Alternate if __slots__ might be an issue (temp list & frozenset):
    attrnames = filter(frozenset(dir(obj)).__contains__, words)
    # Or combine the two to be sure (on Py3, use .keys() instead of .viewkeys())
    # (temp list and set):
    attrnames = filter((vars(obj).viewkeys() | dir(obj)).__contains__, words)

    # Convenient way to get all names discovered at once; returns single object
    # for single attr, tuple of objects for multiple attrs:
    return operator.attrgetter(*attrnames)(obj)

    # If you want a tuple unconditionally, use this instead:
    return tuple(getattr(obj, name) for name in attrnames)

    # Or to only return the first attribute encountered, raising StopIteration
    # if no attributes are found:
    return next(getattr(obj, name) for name in attrnames)

Then usage is:

>>> person = Person("Earl", "Martin", "Jason", 40)
>>> query_obj(person, "What is the name?")
'Earl'  # Would be ('Earl',) in unconditional tuple case
>>> query_obj(person, "What is the name and surname?")
('Earl', 'Martin')  # Would be 'Earl' in single return case