آشنایی سریع با جزییات اشیای پایتون — به زبان ساده
آیا تاکنون پیش آمده که یک شیء پایتون داشته باشید که بخواهید به سرعت با جزییات آن آشنا شوید؟ شاید هم پیش آمده که شیء آشنایی داشتهاید و به دنبال یک متد خاص میگشتهاید، اما نمیدانستهاید آن را چگونه برای گوگل توصیف کنید. زمانی که در زمینه علوم داده روی اشیای پیچیده در کتابخانههایی مانند TensorFlow کار میکنید، بارها با این موقعیت مواجه خواهید شد. در چنین مواردی آرزو میکنید که روش سریعتری برای شناخت جزییات اشیای پایتون در کتابخانههای جدید وجود داشته باشد، چون ممکن است مستندات در دسترس نباشد یا نادرست باشند و یا بررسی آنها زمانبر باشد.
در این مقاله به روش بررسی عمیق اشیا پایتون میپردازیم و یک ابزار CLI قابل نصب از طریق pip معرفی میکنیم که pip dis (+) نام دارد و همه این کارها را برای شما انجام میدهد. اگر میخواهید مستقیماً با این ابزار آشنا شوید میتوانید این بخش را رد کرده و یکراست به سراغ بخش «ابزار بازبینی شیء CLI به نام Peep Dis» بروید.
بررسی شیء
در این بخش به عنوان یک مثال ساده یک کلاس Rectangle یا چند متد و خصوصیت تعریف میکنیم:
1class Rectangle:
2 def __init__(self, a: float, b: float):
3 self.a = a
4 self.b = b
5
6 def area(self) -> float:
7 return self.a * self.b
8
9 def scale(self, factor: float):
10 """ scale the side lengths by `factor` """
11 self.a = factor * self.a
12 self.b = factor * self.b
13
14 def bisect(self):
15 """ reduce a by a factor of 2 to "cut in half" """
16 self.a /= 2
17
18 def __str__(self):
19 return self.__class__.__name__ + str({'a': self.a, 'b': self.b})
تابع dir یک تابع داخلی ساده است که همه خصوصیتها و متدهای یک شیء را به جز __dir__ که overload شده است، فهرستبندی میکند.
این همان خصوصیت است که ادیتورها و IDE-ها برای «تکمیل خودکار» (Auto-complete) مورد استفاده قرار میدهند.
>>> rect = Rectangle(3., 4.) >>> dir(rect) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'area', 'b', 'bisect', 'scale']
خروجی ما لیستی از رشتهها است که خصوصیتها و متدهای شیء را نمایش میدهد که غالباً شامل موارد داخلی هستند. به طور معمول موارد درونی چندان مفید نیستند و فقط باعث افزایش شلوغی میشوند.
فیلتر کردن موارد داخلی
بسته به تعریف موارد درونی میتوان یا از فیلترینگ رشته یا فیلترینگ نوع برای حذف آنها استفاده کرد.
فیلترینگ رشته
1def dir_string_filter(obj):
2 is_magic = lambda x: (x.startswith('__') and x.endswith('__'))
3 return [x for x in dir(obj) if not is_magic(x)]
4>>> dir_string_filter(rect)
5['a', 'area', 'b', 'bisect', 'scale']
فیلترینگ نوع
1from types import BuiltinMethodType
2def dir_type_filter(obj):
3 is_builtin = lambda x: isinstance(getattr(obj, x), BuiltinMethodType)
4 return [x for x in dir(obj) if not is_builtin(x)]
5>>> dir_type_filter(rect)
6['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__repr__', '__setattr__', '__str__', '__weakref__', 'a', 'area', 'b', 'bisect', 'scale']
فیلترینگ رشته، همه متدها و خصوصیتهای جادویی را حذف میکند، در حالی که فیلترینگ به وسیله BuiltinMethodType متدهای داخلی نوشته شده به زبان C را حذف میکند و خصوصیتهای جادویی و بسیاری از متدهای غیر جادویی باقی میمانند. در اغلب موارد متدها و خصوصیتهای جادویی مواردی هستند که میخواهیم حذف کنیم و از این رو از روش فیلترینگ رشته استفاده خواهیم کرد.
1>>> dir_filtered = dir_string_filter(rect)
جداسازی متدها از خصوصیتها
از میان آیتمهایی که پس از فیلترینگ به دست میآیند، ما همچنان نمیدانیم که کدام یک خصوصیت و کدام یک متد هستند. بدین منظور میتوان از تابع داخلی callable (https://docs.python.org/3/library/functions.html#callable) برای فیلتر کردن آنها استفاده کرد.
خصوصیتها
1>>> attrs = [x for x in dir_filtered if not callable(getattr(rect, x))]
2>>> attrs
3['a', 'b']
متدها
1>>> methods = [x for x in dir_filtered if callable(getattr(rect, x))]
2>>> methods
3 ['area', 'bisect', 'scale']
برای دیدن مقادیر خصوصیتها
1>>> attr_outputs = {x: getattr(rect, x) for x in attrs}
2>>> attr_outputs
3{'a': 3.0, 'b': 4.0}
فراخوانی متدها
در مورد متدها دیدن مقادیر خروجی چندان ساده نیست. یکی از ریسکهای فراخوانی گاهبهگاه متدهای تصادفی این است که ممکن است حالت شیء اصلی را تغییر دهند. برای نمونه Rectangle.bisect مقدار None بازگشت خواهد داد، اما اندازه مستطیل را با ضریبی از 2 کاهش میدهد.
1 ...
2 def bisect(self):
3 """ reduce a by a factor of 2 to "cut in half" """
4 self.a /= 2
میتوان با ایجاد یک copy.deepcopy پیش از هر فراخوانی متد، از بروز تغییراتی در شیء اصلی جلوگیری کرد، گرچه این وضعیت میتواند در مورد اشیای بزرگ از نظر محاسباتی سنگین باشد. توجه داشته باشید که متدهایی که متغیرهای کلاس یا متغیرهای سراسری را تغییر میدهند و یا با محیط بیرونیشان تعامل دارند ممکن است تأثیرات پایداری داشته باشند.
تابع get_callable که در ادامه تعریف شده است، شیء اصلی را کپی کرده و متد متصل به آن کپی را بازگشت میدهد که میتواند به صورت مستقل از شیء والدش فراخوانی شود.
1from copy import deepcopy
2def get_callable(obj, name: str):
3 return getattr(deepcopy(obj), name)
متدهایی مانند Rectangle.scale که نیازمند پارامترهای موقعیتی (positional) باشند، چالشی مضاعف ایجاد میکنند.
1 ...
2 def scale(self, factor: float):
3 """ scale the side lengths by factor """
4 self.a = factor * self.a self.b = factor * self.b
ما میتوانیم خروجیهای متدهایی که نیازمند موقعیتهای مختلف هستند را با استفاده از «بررسی همهجانبه قبلی» یا با استفاده از gestfullargspec از ماژول داخلی insepct به دست آوریم تا مشخص شود که کدام اشیا نیازمند آرگومانهای موقعیتی نیستند و تنها آنها را ارزیابی کنیم.
تکنیک 1 فراخوانی متدها: بررسی همهجانبه
1def attempt_method_call(func):
2 try:
3 return str(func())
4 except:
5 return '(failed to evaluate method)'
6>>> outputs = {x: attempt_method_call(get_callable(rect, x)) for x in methods}
7>>> outputs
8{'area': 12.0, 'bisect': None, 'scale': '(failed to evaluate method)', }
چنان که انتظار میرود، area و bisect با موفقیت اجرا شدهاند، در حالی که scale که نیازمند آرگومانهای موقعیتی دیگری است چنین نبوده است.
تکنیک 2 فراخوانی متدها: بررسی جایگاهها
ابتدا باید با getfullargspec آشنا شویم:
1from inspect import getfullargspec
2>>> getfullargspec(rect.scale)
3FullArgSpec(args=['self', 'factor'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={'factor': <class 'float'>})
این متد یک شیء FullArgSpec بازگشت میدهد. args شامل نامهای آرگومانها است. vargs و varkw شامل نامهای آرگومانهای طول متغیر و آرگومانهای کلیدواژه است که به ترتیب با استفاده از عملگرهای * و ** مشخص میشوند. defaults شامل مقادیر پیشفرض برای آرگومانهای کلیدواژه است. kwonlyargs اسامی آرگومانهای صرفا-کلیدواژه را لیست میکند. kwonlydefaults یک دیکشنری است که دارای مقادیر پیشفرض آرگومان صرفاً-کلیدواژه است. annotations یک دیکشنری است که همه «حاشیهنویسیهای نوع» (type annotations) را تعیین میکند.
میتوان از این اطلاعات برای بررسی این نکته که آیا یک متد، آرگومانهای موقعیتی دارد یا نه، استفاده کرد و تنها در صورت عدم وجود چنین چیزی آن را ارزیابی کرد. در آغاز تلاش خواهیم کرد FullArgSpec متد را به دست آوریم گرچه همه انواع قابل فراخوانی پشتیبانی نمیشوند. سپس آرگومانها را استخراج میکنیم و یک تابع کاربردی به نام remove_self_ برای حذف آرگومان selft تعریف میکنیم که در متدهای استاندارد تصریح شده است. با این که این کار در اینجا انجام نیافته، اما میتوانیم با بررسی آرگومان cls از فراخوانی متدهای کلاس اجتناب کنیم. در نهایت اگر آرگومانها مقادیر پیشفرضی داشته باشند، در این صورت هیچ آرگومان موقعیتی وجود ندارد و لذا متد میتواند فراخوانی شود.
1def call_if_no_positionals(func):
2 try:
3 spec = getfullargspec(func)
4 except TypeError:
5 return '(unsupported callable)'
6 args = spec.args
7 _remove_self(args)
8 n_defaults = len(spec.defaults) if spec.defaults else 0
9 # check if all args are kwargs
10 if len(args) == n_defaults:
11 return func()
12 else:
13 return '(requires positional args)'
14
15def _remove_self(arg_list):
16 """ remove implicit `self` argument from list of arg names """
17 if 'self' in arg_list:
18 arg_list.remove('self')
با استفاده از این روش همان نتایجی که در روش بررسی همه جانبه به دست امد را کسب میکنیم:
1>>> method_outputs = {x: call_if_no_positionals(get_callable(rect, x)) for x in methods}
2>>> method_outputs
3{'area': 12.0, 'bisect': None, 'scale': '(requires positional args)'}
استنباط انواع آرگومان
در ادامه تلاش میکنیم نوع هر آرگومان را از روی نوع حاشیهنویسی مقادیر پیشفرض به دست آوریم. ما infer_arg_types را تعریف میکنیم که به صورتی مشابه call_if_no_positionals آغاز میشود، اما به جای فراخوانی متد، یک OrderedDict با انواع استنباط شده مقداردهی میکند.
1from inspect import getfullargspec
2from typing import Dict
3
4def infer_arg_types(func):
5 try:
6 spec = getfullargspec(func)
7 except TypeError:
8 return '(unsupported callable)'
9 arg_types = OrderedDict()
10 args = spec.args
11 _remove_self(args)
12 # infer types from type hints
13 for arg in args:
14 arg_types[arg] = spec.annotations.get(arg, None).__name__
15 # infer types from default args
16 if spec.defaults:
17 for i, v in enumerate(spec.defaults):
18 arg_i = - len(spec.defaults) + i
19 arg = args[arg_i]
20 arg_types[arg] = type(v).__name__
21 if not arg_types:
22 return None
23 return arg_types
این را روی وهلهای از Rectangle خود فرامیخوانیم تا انواع همه متدهایی که نیازمند آرگومان هستند به دست آید چون همه آنها دارای «سرنخ نوع» (type hint) هستند. توجه کنید که اگر دارای سرنخ نوع نبودند، این ترفند تنها برای آرگومانهای کلیدواژه کار میکرد.
1>>> method_arg_types = {x: get_arg_types(getattr(rect, x)) for x in methods}
2>>> method_arg_types
3{'area': None, 'scale': OrderedDict([('factor', 'float')]), 'take_half': None}
ساخت آرگومانها
اگر بخواهیم خروجیهای نمونه متدهایی که نیازمند آرگومانها موقعیتی هستند را ببینیم، میتوانیم از انواع آرگومان که در بخش قبل استنباط شده است استفاده کنیم تا آنها را با بررسی مقادیر نمونه برای هر نوع بسازیم. حتی میتوان در صورت وجود نوع محتوا در حاشیهنویسی حتی تلاش کنیم collection نیز بسازیم.
1from typing import List
2_sample_args = {
3 'float': 1.5,
4 'int': 2,
5 'str': 'abc',
6 'List[int]': [1, 2, 3],
7}
ما یک ForgeError را طوری تعریف میکنیم که هر خطای ناشی از ساخت آرگومان را بتوان به صورت دقیق مدیریت کرد. بدین ترتیب میتوانیم آرگومانها را برای یک کلکسیون از متدها، حتی اگر برخی از آنها کار نکنند بسازیم.
1class ForgeError(ValueError):
2 pass
برای ساخت تابع، یک متد برمیداریم و آرگومانهای نمونه را از sample_args_ برحسب نوع خروجی infer_arg_types بررسی میکنیم. در صورتی که هر نوع آرگومانی فاقد نوع پیشفرض باشند و نوعها را نتوان استنباط کرد، خطاهایی رخ میدهند یا اگر هر نوعی وجود داشته باشند، در sample_args_ وجود نخواهند داشت.
1def forge_args(func, sample_dict):
2 arg_types = infer_arg_types(func)
3 # If no positional arguments
4 if not arg_types:
5 return {}
6 # If not all types could be inferred
7 if not all(arg_types.values()):
8 raise ForgeError(f'Some arguments have unknown types')
9
10 arg_dict = OrderedDict()
11 for i, (arg, type_) in enumerate(arg_types.items()):
12 # check for default values if keyword arg
13 defaults = getfullargspec(func).defaults
14 n_args_remaining = len(arg_types) - i
15 if len(defaults) >= n_args_remaining:
16 arg_dict[arg] = defaults[- n_args_remaining]
17 # if no defaults, attempt to forge from _sample_dict
18 elif type_ in _sample_args:
19 arg_dict[arg] = sample_dict[type_]
20 else:
21 raise ForgeError(
22 f'Unsupported argument type ({type_}) for argument: {arg}')
23 return arg_dict
از آنجا که این تابع نسبتاً پیچیدهای است، این وضعیت میتواند جای خوبی برای برخی تستهای unit باشد.
سپس میتوانیم تابعی تعریف کنیم که یک شیء بگیرد و روی همه متدهای آن بچرخد. این تابع از تابع forge_args ما برای ساخت آرگومانها در مواردی که از رویکرد «بررسی همهجانبه» استفاده میکنند بهره میگیرد و به بررسی دلایل هر گونه شکست میپردازد.
1def forge_and_eval_methods(obj):
2 dir_filtered = dir_string_filter(obj)
3 method_names = filter_methods(obj, dir_filtered)
4 output_dict = {}
5 for name in method_names:
6 method = get_callable(obj, name)
7 try:
8 arg_dict = forge_args(method)
9 output_dict[name] = str(method(**arg_dict))
10 except ForgeError:
11 output_dict[name] = "(Failed to forge args)"
12 except Exception:
13 output_dict[name] = "(Failed to run method with forged args)"
14 return output_dict
در ادامه آن را روی وهله Rectangle امتحان میکنیم:
1>>> forged_outputs = forge_and_eval_methods(rect)
2>>> forged_outputs
3{'area': '12.0', 'bisect': None, 'scale': 'None'}
تفاوت بین این نتیجه و نتیجه قبلی ما چندان بزرگ نیست، اما توجه کنید که scale هم اینک به جای ‘requires positional args’ مقدار ‘None’ را در خروجی ارائه میدهد. دلیل این امر آن است که این متد با موفقیت با آرگومانهای ساخته شده فراخوانی شده است، اما به جای بازگشت دادن هیچ چیز، حالت rect را با تغییر دادن خصوصیت a و b عوض میکند. ردگیری این تغییرات به طوری که بتوان کاری که متدها انجام میدهند را حتی در زمانی که هیچ چیزی بازگشت نمیدهند تشخیص داد بسیار مناسب خواهد بود.
ردگیری تغییرات حالت: تکنیک مقایسه
در این مثال نمونه، بُعد یعنی a و b را از Rectangle تغییر میدهیم، اما از آنجا که متد هیچ بازگشتی ندارد تعیین این که چه اتفاقی افتاده است کار دشواری است. ما میتوانیم این تغییرات را با ذخیره کردن یک کپی از همه خصوصیات شیء پیش از فراخوانی متد و پس از آن مقایسه کردن دریابیم. بدین ترتیب شیء StateComparator را طوری تعریف میکنیم که امکان ذخیره خصوصیتهای جاری را با استفاده از خصوصیت __dict__ به دست دهد، سپس موارد خصوصیتهای حذف، اضافه و تغییر یافته را پس از فراخوانی متد بررسی میکنیم.
1class StateComparator:
2 def __init__(self, obj):
3 self.state = deepcopy(obj.__dict__)
4
5 def compare(self, other):
6 state_1 = self.state
7 state_2 = deepcopy(other.__dict__)
8 new_attrs = {k: v for k, v in state_2.items() if k not in state_1}
9 del_attrs = {k: v for k, v in state_1.items() if k not in state_2}
10 mod_attrs = {k: (v, state_2[k]) for k, v in state_1.items()
11 if v != state_2[k]}
12 change_dict = {}
13 if new_attrs:
14 change_dict['new'] = new_attrs
15 if del_attrs:
16 change_dict['deleted'] = del_attrs
17 if mod_attrs:
18 change_dict['modified'] = mod_attrs
19 return change_dict
پیادهسازی واقعی کمی پیچیدهتر است، زیرا اغلب موارد داخلی دارای خصوصیت __dict__ نیستند.
ما با استفاده از تابع forge_and_eval_methods به عنوان یک قالب میتوانیم یک تابع جدید تعریف کنیم که شامل ردگیری حالت و گزینهای برای فعال یا غیرفعال کردن ساخت آرگومان باشد.
1def call_all_tracked(obj, sample_dict=_sample_args, forge=True):
2 dir_filtered = dir_string_filter(obj)
3 method_names = filter_methods(obj, dir_filtered)
4 output_dict = {}
5 for name in method_names:
6 obj_2 = deepcopy(obj)
7 # store initial state
8 state = StateComparator(obj_2)
9 method = getattr(obj_2, name)
10 try:
11 if forge is True:
12 arg_dict = forge_args(method, sample_dict)
13 else:
14 arg_dict = {}
15 output_dict[name] = str(method(**arg_dict))
16 except ForgeError:
17 output_dict[name] = "(Failed to forge args)"
18 except Exception:
19 output_dict[name] = "(Failed to run method with forged args)"
20 # check for state changes
21 Nchange_dict = state.compare(obj_2)
22 if change_dict:
23 output_dict[name] = {
24 'output': output_dict[name],
25 'state changes': change_dict
26 }
27 return output_dict
در ادامه این مورد را روی rect تست میکنیم:
1>>> call_all_tracked(rect)
2{'area': '12.0', 'bisect': {'state changes': {'modified': {'a': (3.0, 1.5)}}}, 'scale': {'output': {'state changes': {'modified': {'a': (3.0, 4.5), 'b': (4.0, 6.0)}}}}}
اکنون هر تغییر حالت در دیکشنری تعیین شده است و تغییرات به وسیله یک چندتایی که عدد اول نشاندهنده مقدار اولیه و عدد دوم نماینده مقدار نهایی است نمایش مییابند.
متأسفانه ساختن آرگومان از آرگومانهای کلیدواژه و حاشیهنویسی کار دشواری است، زیرا اغلب کدهای پایتون دارای سرنخ نوع نیستند و بخش بزرگ آن از سوی getargspec پشتیبانی نمیشوند. در این موارد، ساخت آرگومان از طریق روش brute force یا استخراج از docstring-ها نیز ممکن است برای قابلیتهای آتی peep dis برنامهریزیشده است.
پرینت کردن صرف docstring-ها راه آسانتری برای درک متدهایی است که در اغلب موارد نیازمند آرگومان هستند. آنها را میتوان به طور سیستماتیک از خصوصیت __doc__ پرینت کرد.
1>>> for x in dir_filtered:
2>>> attr = getattr(x, __doc__, "No docstring")
3>>> print(f'{x}: {attr}')
گنجاندن خروجی بسیار طولانی است و رمزگشایی از آن دشوار است، زیرا دارای کدهای رنگی نیست. خروجی میتواند با termcolor رنگآمیزی شود که برای peep dis نیز استفاده شود.
بازبینی شیء CLI: Peep Dis
در این بخش به بررسی peep dis میپردازیم که در دو حالت مفید واقع میشوند.
شیء اسرارآمیز
ما یک شیء ساده به نام mystery_obj داریم که شامل آرایهای از دماهای مکانی در سانفرانسیسکو است، اما نمیدانیم آنجا کجاست. میتوانیم dir را فراخوانی کنیم و سپس به صورت تکراری هر متد یا خصوصیت را بررسی کنیم. همچنین میتوانیم شیء را peep نیز بکنیم. ما میتوانیم stdtemp را به عنوان خصوصیتی که نیاز داریم به سرعت شناسایی کنیم.
موارد داخلی فیلتر میشوند و خروجیها برای بقیه خصوصیتها و متدها بدون آرگومان موقعیتی پرینت میشوند. متدها به رنگ بنفش و خصوصیتها فیروزهای رنگآمیزی شدهاند. خروجی متدهای نیازمند آرگومان موقعیتی خاکستری میشوند تا امکان شناسایی راحتتر بقیه موارد را پیدا کنیم.
نام متد چیست؟
ما یک DataFrame با ستونهای temp و humidity برای سانفرانسیسکو داریم که میخواهیم به یک مدل داده ظریف برای یک API که میسازیم تبدیل کنیم. یک روش خلاصه برای این کار وجود دارد، اما چیزی از dir مشخص نمیشود و از Stack Overflow نیز نتیجهای عاید نمیشود. اگر DataFrame را peep کنیم، به سرعت مشخص میشود که melt آن متدی است که نیاز داریم.
در ادامه روشهای قدیمی انجام این کارها را بررسی میکنیم:
شیء mystery
1>>> dir(myster_obj)
2['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'itemp', 'mtemp', 'stdtemp', 'temp']
3>>> obj.mtemp
4<bound method WeatherSeries.mtemp of <main.WeatherSeries object at 0x7fdb6ed32748>>
5>>> mystery_obj.mtemp()
6{'min': 67, 'max': 71, 'index min': 0, 'index max': 4, 'len': 6}
7>>> mystery_obj.itemp
8<bound method WeatherSeries.itemp of <__main__.WeatherSeries object at 0x7fdb6ed32780>>
9>>> mystery_obj.itemp()
10TypeError: itemp() missing 1 required positional argument: 'i'
11>>> mystery_obj.itemp(0)
1267
13>>> mystery_obj.temp()
14array([ 0, 67],
15 [ 1, 69],
16 [ 2, 70],
17 [ 3, 70],
18 [ 4, 71],
19 [ 5, 70]])
20>>> mystery_obj.stdtemp()
21TypeError: 'numpy.ndarray' object is not callable
22>>> mystery_obj.stdtemp
23array([67, 69, 70, 70, 71, 70])
نام متد
1>>> df
2 humitidy temp
3 0 65 67
4 1 65 68
5 2 60 68
6 3 60 69
7 4 55 70
8>>> dir(df)
9['T', '_AXIS_ALIASES', '_AXIS_IALIASES', '_AXIS_LEN', '_AXIS_NAMES', '_AXIS_NUMBERS', '_AXIS_ORDERS', '_AXIS_REVERSED', '_AXIS_SLICEMAP', '__abs__', '__add__', '__and__', '__array__', '__array_wrap__', '__bool__', '__bytes__', '__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__div__', '__doc__', '__eq__', '__finalize__', '__floordiv__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__invert__', '__ipow__', '__isub__', '__iter__', '__itruediv__', '__le__', '__len__', '__lt__', '__mod__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__or__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__unicode__', '__weakref__', '__xor__', '_accessors', '_add_numeric_operations', '_add_series_only_operations', '_add_series_or_dataframe_operations', '_agg_by_level', '_agg_doc', '_aggregate', '_aggregate_multiple_funcs', '_align_frame', '_align_series', '_apply_broadcast', '_apply_empty_result', '_apply_raw', '_apply_standard', '_at', '_box_col_values', '_box_item_values', '_builtin_table', '_check_inplace_setting', '_check_is_chained_assignment_possible', '_check_percentile', '_check_setitem_copy', '_clear_item_cache', '_combine_const', '_combine_frame', '_combine_match_columns', '_combine_match_index', '_combine_series', '_combine_series_infer', '_compare_frame', '_compare_frame_evaluate', '_consolidate', '_consolidate_inplace', '_construct_axes_dict', '_construct_axes_dict_for_slice', '_construct_axes_dict_from', '_construct_axes_from_arguments', '_constructor', '_constructor_expanddim', '_constructor_sliced', '_convert', '_count_level', '_create_indexer', '_cython_table', '_dir_additions', '_dir_deletions', '_ensure_valid_index', '_expand_axes', '_flex_compare_frame', '_from_arrays', '_from_axes', '_get_agg_axis', '_get_axis', '_get_axis_name', '_get_axis_number', '_get_axis_resolvers', '_get_block_manager_axis', '_get_bool_data', '_get_cacher', '_get_index_resolvers', '_get_item_cache', '_get_numeric_data', '_get_values', '_getitem_array', '_getitem_column', '_getitem_frame', '_getitem_multilevel', '_getitem_slice', '_gotitem', '_iat', '_iget_item_cache', '_iloc', '_indexed_same', '_info_axis', '_info_axis_name', '_info_axis_number', '_info_repr', '_init_dict', '_init_mgr', '_init_ndarray', '_internal_names', '_internal_names_set', '_is_builtin_func', '_is_cached', '_is_cython_func', '_is_datelike_mixed_type', '_is_mixed_type', '_is_numeric_mixed_type', '_is_view', '_ix', '_ixs', '_join_compat', '_loc', '_maybe_cache_changed', '_maybe_update_cacher', '_metadata', '_needs_reindex_multi', '_obj_with_exclusions', '_protect_consolidate', '_reduce', '_reindex_axes', '_reindex_axis', '_reindex_columns', '_reindex_index', '_reindex_multi', '_reindex_with_indexers', '_repr_data_resource_', '_repr_fits_horizontal_', '_repr_fits_vertical_', '_repr_html_', '_repr_latex_', '_reset_cache', '_reset_cacher', '_sanitize_column', '_selected_obj', '_selection', '_selection_list', '_selection_name', '_series', '_set_as_cached', '_set_axis', '_set_axis_name', '_set_is_copy', '_set_item', '_setitem_array', '_setitem_frame', '_setitem_slice', '_setup_axes', '_shallow_copy', '_slice', '_stat_axis', '_stat_axis_name', '_stat_axis_number', '_try_aggregate_string_function', '_typ', '_unpickle_frame_compat', '_unpickle_matrix_compat', '_update_inplace', '_validate_dtype', '_values', '_where', '_xs', 'a', 'abs', 'add', 'add_prefix', 'add_suffix', 'agg', 'aggregate', 'align', 'all', 'any', 'append', 'apply', 'applymap', 'as_blocks', 'as_matrix', 'asfreq', 'asof', 'assign', 'astype', 'at', 'at_time', 'axes', 'b', 'between_time', 'bfill', 'blocks', 'bool', 'boxplot', 'clip', 'clip_lower', 'clip_upper', 'columns', 'combine', 'combine_first', 'compound', 'consolidate', 'convert_objects', 'copy', 'corr', 'corrwith', 'count', 'cov', 'cummax', 'cummin', 'cumprod', 'cumsum', 'describe', 'diff', 'div', 'divide', 'dot', 'drop', 'drop_duplicates', 'dropna', 'dtypes', 'duplicated', 'empty', 'eq', 'equals', 'eval', 'ewm', 'expanding', 'ffill', 'fillna', 'filter', 'first', 'first_valid_index', 'floordiv', 'from_csv', 'from_dict', 'from_items', 'from_records', 'ftypes', 'ge', 'get', 'get_dtype_counts', 'get_ftype_counts', 'get_value', 'get_values', 'groupby', 'gt', 'head', 'hist', 'iat', 'idxmax', 'idxmin', 'iloc', 'index', 'info', 'insert', 'interpolate', 'is_copy', 'isin', 'isnull', 'items', 'iteritems', 'iterrows', 'itertuples', 'ix', 'join', 'keys', 'kurt', 'kurtosis', 'last', 'last_valid_index', 'le', 'loc', 'lookup', 'lt', 'mad', 'mask', 'max', 'mean', 'median', 'melt', 'memory_usage', 'merge', 'min', 'mod', 'mode', 'mul', 'multiply', 'ndim', 'ne', 'nlargest', 'notnull', 'nsmallest', 'nunique', 'pct_change', 'pipe', 'pivot', 'pivot_table', 'plot', 'pop', 'pow', 'prod', 'product', 'quantile', 'query', 'radd', 'rank', 'rdiv', 'reindex', 'reindex_axis', 'reindex_like', 'rename', 'rename_axis', 'reorder_levels', 'replace', 'resample', 'reset_index', 'rfloordiv', 'rmod', 'rmul', 'rolling', 'round', 'rpow', 'rsub', 'rtruediv', 'sample', 'select', 'select_dtypes', 'sem', 'set_axis', 'set_index', 'set_value', 'shape', 'shift', 'size', 'skew', 'slice_shift', 'sort_index', 'sort_values', 'sortlevel', 'squeeze', 'stack', 'std', 'style', 'sub', 'subtract', 'sum', 'swapaxes', 'swaplevel', 'tail', 'take', 'to_clipboard', 'to_csv', 'to_dense', 'to_dict', 'to_excel', 'to_feather', 'to_gbq', 'to_hdf', 'to_html', 'to_json', 'to_latex', 'to_msgpack', 'to_panel', 'to_period', 'to_pickle', 'to_records', 'to_sparse', 'to_sql', 'to_stata', 'to_string', 'to_timestamp', 'to_xarray', 'transform', 'transpose', 'truediv', 'truncate', 'tshift', 'tz_convert', 'tz_localize', 'unstack', 'update', 'values', 'var', 'where', 'xs']
ممکن است ده دقیقه طول بکشد تا متوجه شویم که آن چه نیاز داریم df.melt است.
1>>> df.melt()
2 variable value
3 0 humitidy 65
4 1 humitidy 65
5 2 humitidy 60
6 3 humitidy 60
7 4 humitidy 55
8 5 humitidy 55
9 6 temp 67
10 7 temp 69
11 8 temp 70
12 9 temp 70
13 10 temp 71
14 11 temp 70
بدین ترتیب به پایان این مقاله میرسیم. امیدواریم از مطالعه این راهنما بهره کافی را برده باشید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی پایتون Python
- گنجینه آموزشهای برنامهنویسی پایتون (Python)
- مجموعه آموزشهای برنامهنویسی
- ۱۰ گام تکمیلی ایجاد پروژه متن باز پایتون — راهنمای کاربردی
- زبان برنامه نویسی پایتون (Python) — از صفر تا صد
==