2018年4月30日 星期一

[ Python 文章收集 ] Dynamic Attributes in Python

Source From Here 
Preface 
One of the strengths of a dynamic language is that it allows you to more easily work introspection and light weight meta-programming into your every day code. In Python, one of the primary ways of taking advantage of the dynamic nature of the language is through attribute access. 
Note: this is part one in a series of posts about basic Python functionality. 

In most cases, to get an attribute of an object, you just want to use obj.field. But if you don’t know the name of the field until runtime, you can use getattr(obj, 'field'): 
  1. def print_field(obj, field):  
  2.     try:  
  3.         print getattr(obj, field)  
  4.     except AttributeError:  
  5.         print 'No %s field' % field  
This is a fairly common pattern, so you can avoid the extra try/catch and use the third default parameter: 
  1. def print_field(obj, field):  
  2.     print getattr(obj, field, 'No %s field' % field)  
Both attribute access methods are virtually identical in terms of performance. The regular method produces slightly cleaner code, so normally you would use that. Besides, when do you NOT know the names of the fields you want to access ahead of time? 

More 
If you’re dealing with data, you don’t always know. For example, say you’re mapping URLs to view methods. If the user hits /user/123/settings, you could route that to a view function as follows: 
  1. class ViewClass(object):  
  2.   
  3.     def route(request):  
  4.         return getattr(self, request.url.split('/')[-1], 'not_found_404')(request)  
  5.   
  6.     def settings(request):  
  7.         return HttpResponse('Here is your settings page!')  
  8.   
  9.     def not_found_404(request):  
  10.         return HttpResponse('404 Page Not Found', code=404)  
Of course, you could always do this with a pre-defined set of URLs, but the point is that you have a built-in way to avoid that code duplication. In general, this is known as keeping your code DRY. For example, notice the duplication of tokens in code like the following: 
  1. obj.first_name.first_child.value = 'Chase'  
  2. obj.last_name.first_child.value = 'Seibert'  
  3. obj.phone.first_child.value = '(555) 123-4567'  
Instead, you could do something like the following: 
  1. for (field, value) in (('first_name', 'Chase'), ('last_name', 'Seibert'), ('phone', '(555) 123-4567')):  
  2.     getattr(obj, field).first_child.value = value  
While this is certainly more code, for a larger number of lines, there will be a code savings. It’s also easy to refactor all the obj.field lines at once, if for example you need to change it to obj.field.set(value). You can also make use of dynamic attributes on the class side by over-riding __getattr__. 
  1. class Counter(object):  
  2.     def __getattr__(self, name):  
  3.         ''' will only get called for undefined attributes '''  
  4.         setattr(self, name) = 0  
  5.   
  6. counter = Counter()  
  7. counter.foo = counter.foo + 100  
  8. print counter.foo  # prints '100'  
There is an alternate magic method called __getattribute__ that fires even for attributes that are already declared. But be careful, it’s easy to get into an infinite recursion loop here. 
  1. class Logger(object):  
  2.     def __init__(self):  
  3.         print('Initialized Logger...')  
  4.         # self.foobar = 100  
  5.   
  6.     def __getattribute__(self, name):  
  7.         print('Accessed attribute {}'.format(name))  
  8.         return object.__getattribute__(self, name)  
  9.   
  10. logger = Logger()  
  11. logger.foobar = 10  
  12. print("logger.foobar = {}".format(logger.foobar))  
Output: 
Initialized Logger... 
Accessed attribute foobar 
logger.foobar = 10

This is a trivial example, better implemented with decorators. But that is a subject for another post! 

Finally, there is a sister to getattr called setattr. As you would expect, this will set attributes by name. Here is a quick example: 
  1. class MyModel(object):  
  2.   
  3.     @classmethod  
  4.     def from_kwargs(cls, **kwargs):  
  5.         obj = cls()  
  6.         for (field, value) in kwargs.items():  
  7.             setattr(self, field, value)  
  8.         return obj  
  9.   
  10. model = MyModel.from_kwargs(foo=1, bar=2)  
  11. print model.foo, model.bar  # prints '1, 2'  


沒有留言:

張貼留言

[ Py DS ] Ch1 - IPython: Beyond Normal Python

Source From  Here   Keyboard Shortcuts in the IPython Shell   If you spend any amount of time on the computer, you’ve probably found a u...