PDA

View Full Version : property@ چيكار ميكنه؟



arman54
شنبه 14 شهریور 1394, 00:05 صبح
سلام ...
توي پايتون3 از دكوريتور property@ در چه مواردي استفاده ميشه و چه كاري رو انجام ميده؟

HackNetProg
جمعه 20 شهریور 1394, 16:13 عصر
سلام
سوال خوبی هست
در واقع موضوع بعدی مقاله های من بود.


http://barnamenevis.org/showthread.php?503925-%D9%87%D9%81%D8%AA%D9%87-%D8%A7%DB%8C-%DB%8C%DA%A9-%D9%85%D9%82%D8%A7%D9%84%D9%87-%D8%A7%D8%B2-%D9%BE%D8%A7%DB%8C%D8%AA%D9%88%D9%86


قبل اینکه بریم سراغ مبحث property اول باید Descriptor ها را بفهمیم. به طور حیلی خلاصه کاری که این انجام میده اینه که ترتیب lookup پایتون رو عوض میکنه. به مثال زیر دقت کنید.



class RevealAccess(object):
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
"""


def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name


def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val


def __set__(self, obj, val):
print('Updating', self.name)
self.val = val


>>> class MyClass(object):
x = RevealAccess(10, 'var "x"')
y = 5


>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5


وقتی از m.x استفاده میکنید به جای اینکه مثل کلاس های معمولی به سراغ این attribute بره و مقدارشو برگردونه؛ رفت به سراغ __get__ و اونو اجرا کرد و راجب __set__ هم همینطور.

property ها هم نوعی پیاده سازی از Descriptor هستن. این کد خالص پایتون برای property ها :



class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"


def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc


def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)


def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)


def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)


def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)


def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)


def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)


خب حالا این به چه دردی میخوره ؟
طبق مفاهیم شی گرایی هر شی ای ویژگی های خاص خودشو داره که جداگانه از دیگران نگهداری میشه یعنی :



class C:
def __init__(self, name):
self._x = name


def x(self):
return self._x


def x(self, value):
self._x = value


def x(self):
del self._x


#کد شماره 1
>>> instance=C('pd')
>>> instance._x
'pd'


>>> instance_Plus=C('mr pd')
>>> instance_Plus._x
'mr pd'




اگر با طراحی شی گرا اشنا باشید یکی از مهم ترین نکات پایین نگه داشتن coupling هست ، اصلا کلا مفهوم کلاس ها برای همین موضوعات به وجود اومدن. یعنی encapsulation!
در زبان هایی مثل جاوا مفهوم private داره :


public class Class()
{


private int number = 0;




public int getNumber()
{
return number;
}


}

معمولا همین کارو میکنن attribute های private و methodهای public . و اصل قضیه اینجاست که از خارج از کلاس به private ها دست رسی ندارید.
اما این قضیه کلا در پایتون وجود نداره ، یعنی attribute های private رو میشه از بیرون کلاس بهشون دست رسی داشت :



class C:
_private_attribute='hide me!'


x._private_attribute #=> 'hide me!'



یعنی پایتون میگه این private_attribute از نوع private هست ! طراح اینطور خواسته خب باشه اما اگر لازم شد شما به عنوان برنامه نویس میتونی بهش دست رسی داشته باشی:شیطان:
یعنی شما میفهمی که طراح خواسته اینو مخفی کنه ولی تصمیم نهایی با خودته اگر لازمه ازش استفاده کن.
اینا که گفتم یعنی اینکه اگر با getter , setter جاوا اشنا هستید کلا اینا رو از ذهنتون دور کنید، تویه پایتنون ازین چیزا خبری نیست !
اما از property ها در پایتون به عنوان getter , setter استفاده میشه ! اما دقت کنید به سبک پایتون نه جاوا !



class C:
def __init__(self):
self._name = None


@property
def name(self):
"""I'm the 'x' property."""
return self._name


@name.setter
def name(self, value):
self._name = value


@name.deleter
def name(self):
del self._name


>>> instance=C()
>>> instance.name='pd'
>>> instance.name
'pd'

این نوع مقدار دهی به name در این کد رو با کد شماره 1 مقایسه کنید.
اما دقت کنید هم چنان دستور زیر باز هم مشکلی ندارد :



>>> instance._name
'pd'




به طور خلاصه اگر بخوام property رو تعریف کنم میگم که :
setter o getter به سبک پایتون رو میگن property .

arman54
جمعه 20 شهریور 1394, 21:04 عصر
تشكر ...
ولي وقتي ميخايم داخل كلاس private بسازيم بايد به اولش 2تا آندرلاين بديم.مثل private_attribute__
و همچنين از بيرون كلاس هم نميشه بطور مستقيم بهشون دسترسي داشت مگر اينكه داخل يه تابع نوشته بشن و تابع پس از فراخوني روش تغييرات رو انجام بده.

n.nowroozi
شنبه 21 شهریور 1394, 07:38 صبح
تشكر ...
ولي وقتي ميخايم داخل كلاس private بسازيم بايد به اولش 2تا آندرلاين بديم.مثل private_attribute__
و همچنين از بيرون كلاس هم نميشه بطور مستقيم بهشون دسترسي داشت مگر اينكه داخل يه تابع نوشته بشن و تابع پس از فراخوني روش تغييرات رو انجام بده.
توی پایتون مفاهیمی مثل پرایوت و پابلیک نداریم اونجور که توی زبانهایی مثل جاوا یا c , .. این دوتا آندرلاین هم صرفا جهت خوانایی کد و همون pep های پایتون هست وگرنه به همین آبجکتها هم از بیرون دسترسی دارید.

HackNetProg
شنبه 21 شهریور 1394, 09:22 صبح
تشكر ...
ولي وقتي ميخايم داخل كلاس private بسازيم بايد به اولش 2تا آندرلاين بديم.مثل private_attribute__
و همچنين از بيرون كلاس هم نميشه بطور مستقيم بهشون دسترسي داشت مگر اينكه داخل يه تابع نوشته بشن و تابع پس از فراخوني روش تغييرات رو انجام بده.

دوتا نکته رو توجه داشته باشید اولا پایتون ، جاوا نیست! یعنی با تفکر کلاس های جاوا تویه پایتون کد ننویسید . عرض کردم تویه پایتون خبری از private و ... نیست (اون چیزی که در جاوا هست)
دوما به طور خلاصه بخوام بگم تویه پایتون مفهومی به نام کلاس( که در جاوا هست ) و قوانین اون کلاس ها وجود نداره !
عجبیه ولی واقعیت . تو پایتون همه چیز شی هست، حتی کلاس ها هم شی اند(نسخه 3 البته) .
شما با اشیا کار میکنید نه با کلاس . یعنی :



class Foo():
pass


>>> x=Foo()
>>> x.name='on-fly'
>>> def method(self):
print(self.name)



>>> x.method()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
x.method()
AttributeError: 'Foo' object has no attribute 'method'
>>> Foo.method=method
>>> x.method()
on-fly
>>>


اما راجب چیزی که گفتید توضیحات بیشتر از خود رفرنس اصلی:


9.6. Private Variables
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python
code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It
should be considered an implementation detail and subject to change without notice.



Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for
such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced
with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic
position of the identifier, as long as it occurs within the definition of a class.



Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. For example:


class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)


def update(self, iterable):
for item in iterable:
self.items_list.append(item)


__update = update # private copy of original update() method


class MappingSubclass(Mapping):


def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)


Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be
useful in special circumstances, such as in the debugger.



Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class; this is similar to the effect of the global
statement, the effect of which is likewise restricted to code that is byte-compiled together. The same restriction applies to getattr(), setattr() and delattr(), as well as
when referencing __dict__ directly.

arman54
شنبه 21 شهریور 1394, 09:50 صبح
توی پایتون مفاهیمی مثل پرایوت و پابلیک نداریم اونجور که توی زبانهایی مثل جاوا یا c , .. این دوتا آندرلاین هم صرفا جهت خوانایی کد و همون pep های پایتون هست وگرنه به همین آبجکتها هم از بیرون دسترسی دارید.

وقتي 2تا آندرلاين ميديم يعني private تعريف كرديم و از بيرون كلاس هم دسترسي نميده.(با ورژن3 امتحان كردم)

HackNetProg
شنبه 21 شهریور 1394, 10:02 صبح
پس یکبارم اینطوری امتحان کنید :


class X():
def __init__ (self,name):
self.__priv=name


>>>obj=X('pd')
>>> obj._X__priv
'pd'


شما متن خود سایت پایتون هم قبول ندارید ؟؟!!


“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python.

arman54
شنبه 21 شهریور 1394, 10:11 صبح
آره اين درسته.
بالا گفتم كه بطور مستقيم نميشه دسترسي داشت ولي با استفاده از توابع ...