لن اكتب الكثير عن الرفلكشن في هذا الموضوع لاني الان لست في المزاج المناسب لعمل ذلك .. لهذا يمكن اعتبار هذا الموضوع من قبيل المقبلات او المشهيات لعل البعض تنفتح شهيته و يكتب عن الموضوع (ان كان ملما به) او يبدأ بالبحث اكثر فيه ان كان يراه لاول مرة او كانت معلوماته عن الموضوع قليلة ..
الرفلكشن reflection مصطلح يطلق و يقصد به قدرة الكود على اكتشاف خصائص كود اخر و تغييره ..
مثلا, اكتشاف الـ functions الموجودة في module او class محدد .. و اضافة حقول او فنكشنز جديدة .. الخ
اعطيكم هذا الكود اللذي كتبته مؤخرا (و اخذت الفكرة حين كنت اقرا عن optparser اللذي هو جزء من الـ library في البايثون)
- class Option:
- def __init__(self, name = '', type = str, default = None, validator = lambda x:x, option_string = None):
- self.name = name
- self.type = type
- self.default = default
- self.validator = validator
- self.option_string = option_string or name
- class ParsedOptions: pass
- class Options:
- def __init__(self, option_list=[]):
- self.option_list = option_list
- def add_option( self, **kwargs ):
- self.option_list.append( Option( **kwargs ) )
- def parse( self, getdict ):
- options = ParsedOptions()
- for opt in self.option_list:
- val = getdict.get(opt.option_string, opt.default)
- if opt.type: val = opt.type(val) #convert to base type ..
- if opt.validator: val = opt.validator(val)
- setattr( options, opt.name, val )
- return options
الفكرة من هذا الكود هو ان يستخدم في تطبيقات الوب, لتسهيل قرائة الـ GET و ما شابهه ..
على فرض انك تعمل ضمن فريمورك معين لبرمجة الوب في البايثون و ان الفريموورك قام بوضع جميع الـ get arguments في دكشنري و ليكن اسمه GET
بدل من ان تقوم بتحليل كل بارامتر على حدة و تحويله للنوع المناسب ..
page = int( GET.get('page',1) )و هو امر يصبح مزعجا بعد فترة من الزمن ..
يمكننا عن طريق الرفلكشن ان نقرا كل البارامترات اللتي نريد و نحولها الى fields ضمن اوبجكت محدد ...
يعني تخيل معي ان تكتب
options = parse_options( GET );
ليصبح عندك بعدها options.page و هو متغير تم انشائه ديناميكيا (اثناء عمل البرنامج) و يحمل القيمة المخزنة في
GET['page']
على كل حال, حتى لو لم يكن هذا التطبيق مفيدا جدا ... فهو مثال على الاقل لما يمكن ان يفعله الرفلكشن
على كل حال, هناك تتمة للكود,ساضعها و اضع معها الـ test cases لعلها توضح بعض الطرق اللتي يمكن بها استخدام الكود
و الفكرة من الكود حتى لو لم تكن مفيدة عمليا لكن كما قلت هي توضح ما يمكن ان يفعله الرفلكشن
- class Option:
- def __init__(self, name = '', type = str, default = None, validator = lambda x:x, option_stri
ng = None): - self.name = name
- self.type = type
- self.default = default
- self.validator = validator
- self.option_string = option_string or name
- class ParsedOptions: pass
- class Options:
- def __init__(self, option_list=[]):
- self.option_list = option_list
- def add_option( self, **kwargs ):
- self.option_list.append( Option( **kwargs ) )
- def parse( self, getdict ):
- options = ParsedOptions()
- for opt in self.option_list:
- val = getdict.get(opt.option_string, opt.default)
- if opt.type: val = opt.type(val) #convert to base type ..
- if opt.validator: val = opt.validator(val)
- setattr( options, opt.name, val )
- return options
- def parse_options( option_list, getdict ):
- return Options( option_list ).parse( getdict )
- def get_class_options( klass ):
- option_list = []
- for attr_name in dir(klass):
- attr = getattr(klass,attr_name)
- if isinstance( attr, Option ):
- option = attr
- if not option.name:
- option.name = attr_name
- option.option_string = option.option_string or option.name #warning: dup code
- option_list.append(option)
- return option_list
- def parse_class_options( klass, getdict ):
- return parse_options( get_class_options( klass ), getdict )
- import unittest
- class OptionsTest( unittest.TestCase ):
- def test_options(self):
- opts = Options()
- opts.add_option( name = "page", type = int, default = 1 )
- opts.add_option( name = "count", type = int, default = 25 )
- opts.add_option( name = "slug", type = str, default = "" )
- gdict = {}
- parsed_options = opts.parse( gdict )
- self.assertEqual( 1, parsed_options.page )
- self.assertEqual( 25, parsed_options.count )
- self.assertEqual( "", parsed_options.slug )
- gdict['page'] = '4'
- parsed_options = opts.parse( gdict )
- self.assertEqual( 4, parsed_options.page )
- self.assertEqual( 25, parsed_options.count )
- self.assertEqual( "", parsed_options.slug )
- gdict['slug'] = 'main'
- parsed_options = opts.parse( gdict )
- self.assertEqual( 4, parsed_options.page )
- self.assertEqual( 25, parsed_options.count )
- self.assertEqual( "main", parsed_options.slug )
- gdict['count'] = '35'
- parsed_options = opts.parse( gdict )
- self.assertEqual( 4, parsed_options.page )
- self.assertEqual( 35, parsed_options.count )
- self.assertEqual( "main", parsed_options.slug )
- del gdict['page']
- parsed_options = opts.parse( gdict )
- self.assertEqual( 1, parsed_options.page )
- self.assertEqual( 35, parsed_options.count )
- self.assertEqual( "main", parsed_options.slug )
- def test_klass_options( self ):
- class PageOptions:
- page = Option( type=int, default=1 )
- count = Option( type=int, default = 14 )
- slug = Option( default = "" )
- gdict = {}
- gdict['page'] = '5'
- parsed_options = parse_class_options( PageOptions, gdict )
- self.assertEqual( 5, parsed_options.page )
- self.assertEqual( 14, parsed_options.count )
- self.assertEqual( "", parsed_options.slug )
- gdict['count'] = '50'
- gdict['slug'] = 'all'
- parsed_options = parse_class_options( PageOptions, gdict )
- self.assertEqual( 5, parsed_options.page )
- self.assertEqual( 50, parsed_options.count )
- self.assertEqual( "all", parsed_options.slug )
- if __name__ == '__main__':
- unittest.main()
اعتذر ان كان الموضوع مخربط او غير مرتب جيدا لكني اردت فقط ان اطرح بعض المواضيع لتحريك المياه في القسم الجديد لعله ينال حظه من الانتباه و يصبح قسم حقيقي
