ด้วยคุณสมบัติของ python ฉันสามารถทำให้เป็นเช่นนั้นได้
obj.y
เรียกใช้ฟังก์ชันแทนที่จะส่งคืนค่า
มีวิธีดำเนินการกับโมดูลหรือไม่? ฉันมีกรณีที่ฉันต้องการ
module.y
เพื่อเรียกใช้ฟังก์ชันแทนที่จะส่งคืนค่าที่เก็บไว้ที่นั่น
ด้วยคุณสมบัติของ python ฉันสามารถทำให้เป็นเช่นนั้นได้
obj.y
เรียกใช้ฟังก์ชันแทนที่จะส่งคืนค่า
มีวิธีดำเนินการกับโมดูลหรือไม่? ฉันมีกรณีที่ฉันต้องการ
module.y
เพื่อเรียกใช้ฟังก์ชันแทนที่จะส่งคืนค่าที่เก็บไว้ที่นั่น
คำตอบ:
เฉพาะอินสแตนซ์ของคลาสสไตล์ใหม่เท่านั้นที่สามารถมีคุณสมบัติได้ คุณสามารถทำให้งูใหญ่เชื่อเช่นตัวอย่างเป็นโมดูลโดย stashing sys.modules[thename] = theinstance
ใน ตัวอย่างเช่นไฟล์โมดูล m.py ของคุณอาจเป็น:
import sys
class _M(object):
def __init__(self):
self.c = 0
def afunction(self):
self.c += 1
return self.c
y = property(afunction)
sys.modules[__name__] = _M()
types.ModuleType
ที่แสดงในคำตอบที่คล้ายกันมากของ @ Unknown หรือไม่
builtins.module
ซึ่งตัวเองเป็นอินสแตนซ์ของtype
(ซึ่งเป็นคำจำกัดความของคลาสสไตล์ใหม่) ปัญหาคือว่าคุณสมบัติจะต้องอยู่ในระดับที่ไม่อินสแตนซ์: ถ้าคุณทำf = Foo()
, f.some_property = property(...)
ก็จะล้มเหลวในทางเดียวกันเช่นถ้าคุณอย่างไร้เดียงสาวางไว้ในโมดูล วิธีแก้ปัญหาคือใส่ไว้ในคลาส แต่เนื่องจากคุณไม่ต้องการให้โมดูลทั้งหมดมีคุณสมบัติคุณจึงเป็นคลาสย่อย (ดูคำตอบของ Unknown)
globals()
(ทำให้คีย์ยังคงอยู่ แต่รีเซ็ตค่าเป็นNone
) เมื่อการผูกชื่อใหม่sys.modules
เป็นปัญหาของ Python 2 - Python 3.4 ทำงานได้ตามที่ตั้งใจไว้ หากคุณต้องการเข้าถึงคลาสอ็อบเจ็กต์ใน Py2 ให้เพิ่มเช่น_M._cls = _M
ทันทีหลังclass
คำสั่ง (หรือซ่อนไว้ในเนมสเปซอื่น ๆ ) และเข้าถึงได้ตามself._cls
วิธีการที่ต้องการ ( type(self)
อาจใช้ได้ แต่ไม่ใช่ถ้าคุณทำคลาสย่อยด้วย_M
) .
ฉันจะทำสิ่งนี้เพื่อสืบทอดคุณสมบัติทั้งหมดของโมดูลอย่างถูกต้องและถูกระบุอย่างถูกต้องโดย isinstance ()
import types
class MyModule(types.ModuleType):
@property
def y(self):
return 5
>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5
จากนั้นคุณสามารถแทรกสิ่งนี้ลงใน sys.modules:
sys.modules[__name__] = MyModule(__name__) # remember to instantiate the class
__file__
ที่ต้องกำหนดด้วยตนเอง (2) การนำเข้าที่ทำในโมดูลที่มีคลาสจะไม่สามารถ "มองเห็นได้" ในระหว่างรันไทม์ ฯลฯ ...
types.ModuleType
, ใด ๆ (แบบใหม่) ชั้นจะทำ คุณลักษณะของโมดูลพิเศษที่คุณหวังจะสืบทอดคืออะไร?
__init__
isinstance
เนื่องจากPEP 562ถูกนำไปใช้ใน Python> = 3.7 ตอนนี้เราสามารถทำได้
ไฟล์: module.py
def __getattr__(name):
if name == 'y':
return 3
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
other = 4
การใช้งาน:
>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "module.py", line 4, in __getattr__
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'
โปรดทราบว่าถ้าคุณละเว้นraise AttributeError
ใน__getattr__
ฟังก์ชั่นที่มันหมายถึงฟังก์ชั่นนี้จบลงด้วยreturn None
แล้วจะได้รับค่าของmodule.nosuch
None
จากคำตอบของ John Lin :
def module_property(func):
"""Decorator to turn module functions into properties.
Function names must be prefixed with an underscore."""
module = sys.modules[func.__module__]
def base_getattr(name):
raise AttributeError(
f"module '{module.__name__}' has no attribute '{name}'")
old_getattr = getattr(module, '__getattr__', base_getattr)
def new_getattr(name):
if f'_{name}' == func.__name__:
return func()
else:
return old_getattr(name)
module.__getattr__ = new_getattr
return func
การใช้งาน (สังเกตขีดล่างนำหน้า) ในthe_module.py
:
@module_property
def _thing():
return 'hello'
จากนั้น:
import the_module
print(the_module.thing) # prints 'hello'
ขีดล่างนำหน้าเป็นสิ่งที่จำเป็นในการแยกความแตกต่างของฟังก์ชัน ized คุณสมบัติจากฟังก์ชันเดิม ฉันคิดวิธีกำหนดตัวระบุใหม่ไม่ได้เนื่องจากในช่วงเวลาของการดำเนินการมัณฑนากรยังไม่ได้รับมอบหมาย
โปรดทราบว่า IDE จะไม่ทราบว่ามีคุณสมบัติอยู่และจะแสดงสีแดง wavies
@property def x(self): return self._x
แล้วฉันคิดว่าการdef thing()
ไม่มีขีดล่างเป็นเรื่องธรรมดากว่า และคุณสามารถสร้างมัณฑนากร "module property setter" ในคำตอบของคุณได้หรือไม่?
def thing()
คำแนะนำของคุณ ปัญหาคือว่า__getattr__
เพียงได้รับการเรียกร้องให้มีคุณลักษณะที่ขาดหายไป แต่หลังจาก@module_property def thing(): …
รันthe_module.thing
ถูกกำหนดดังนั้นgetattrจะไม่ถูกเรียก เราจำเป็นต้องลงทะเบียนthing
ในมัณฑนากรแล้วจึงลบออกจากเนมสเปซของโมดูล ฉันพยายามคืนNone
จากมัณฑนากร แต่thing
ถูกกำหนดให้เป็นNone
ไฟล์. อาจทำได้@module_property def thing(): … del thing
แต่ฉันพบว่าแย่กว่าการใช้thing()
เป็นฟังก์ชัน
__getattribute__
" ขอบคุณ.
กรณีการใช้งานทั่วไปคือ: การเสริมสร้างโมดูลที่มีอยู่ (ขนาดใหญ่) ด้วยคุณลักษณะแบบไดนามิกบางอย่าง (น้อย) โดยไม่ต้องเปลี่ยนโมดูลทั้งหมดให้เป็นเค้าโครงคลาส น่าเสียดายที่แพทช์คลาสโมดูลที่เรียบง่ายที่สุดเช่นsys.modules[__name__].__class__ = MyPropertyModule
ล้มเหลวด้วยTypeError: __class__ assignment: only for heap types
. ดังนั้นการสร้างโมดูลจึงจำเป็นต้องเดินสายใหม่
วิธีนี้ทำได้โดยไม่ต้องใช้ตะขอนำเข้า Python เพียงแค่มี prolog อยู่ด้านบนของโค้ดโมดูล:
# propertymodule.py
""" Module property example """
if '__orgmod__' not in globals():
# constant prolog for having module properties / supports reload()
print "PropertyModule stub execution", __name__
import sys, types
class PropertyModule(types.ModuleType):
def __str__(self):
return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
modnew = PropertyModule(__name__, __doc__)
modnew.__modclass__ = PropertyModule
modnew.__file__ = __file__
modnew.__orgmod__ = sys.modules[__name__]
sys.modules[__name__] = modnew
exec sys._getframe().f_code in modnew.__dict__
else:
# normal module code (usually vast) ..
print "regular module execution"
a = 7
def get_dynval(module):
return "property function returns %s in module %r" % (a * 4, module.__name__)
__modclass__.dynval = property(get_dynval)
การใช้งาน:
>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule) # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"
หมายเหตุ: สิ่งที่ต้องการfrom propertymodule import dynval
จะสร้างสำเนาของหลักสูตรที่ถูกแช่แข็งซึ่งสอดคล้องกับdynval = someobject.dynval
proxy_tools
proxy_tools
พยายามแพคเกจเพื่อให้@module_property
การทำงาน
มันติดตั้งด้วย
pip install proxy_tools
ใช้การปรับเปลี่ยนตัวอย่างเล็กน้อยของ @ Marein ในthe_module.py
เรา
from proxy_tools import module_property
@module_property
def thing():
print(". ", end='') # Prints ". " on each invocation
return 'hello'
ตอนนี้จากสคริปต์อื่นฉันสามารถทำได้
import the_module
print(the_module.thing)
# . hello
วิธีนี้ไม่ได้โดยไม่มีข้อแม้ กล่าวคือthe_module.thing
เป็นไม่สตริง ! เป็นproxy_tools.Proxy
วัตถุที่มีการลบล้างเมธอดพิเศษเพื่อให้เลียนแบบสตริง นี่คือการทดสอบพื้นฐานบางส่วนที่แสดงให้เห็นประเด็น:
res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]
print(type(res))
# <class 'proxy_tools.Proxy'>
print(isinstance(res, str))
# False
print(res)
# . hello
print(res + " there")
# . hello there
print(isinstance(res + "", str))
# . True
print(res.split('e'))
# . ['h', 'llo']
ภายในฟังก์ชันดั้งเดิมจะถูกเก็บไว้ที่ the_module.thing._Proxy__local
:
print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>
จริงๆแล้วฉันงงงันว่าทำไมโมดูลถึงไม่มีฟังก์ชันนี้ในตัวฉันคิดว่าประเด็นสำคัญของเรื่องนี้the_module
คือตัวอย่างของtypes.ModuleType
คลาส การตั้งค่า "คุณสมบัติโมดูล" เป็นการตั้งค่าคุณสมบัติบนอินสแตนซ์ของคลาสนี้แทนที่จะเป็นค่าtypes.ModuleType
คลาสเอง สำหรับรายละเอียดเพิ่มเติมโปรดดูคำตอบนี้นี้
เราสามารถใช้คุณสมบัติtypes.ModuleType
ดังต่อไปนี้ได้จริงแม้ว่าผลลัพธ์จะไม่ดี เราไม่สามารถแก้ไขประเภทในตัวได้โดยตรง แต่เราสามารถสาปแช่งได้:
# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))
สิ่งนี้ทำให้เรามีคุณสมบัติที่มีอยู่ในโมดูลทั้งหมด มันค่อนข้างไม่เข้าใจเนื่องจากเราทำลายพฤติกรรมการตั้งค่าในทุกโมดูล:
import sys
print(sys.thing2)
# hi from sys
sys.thing2 = 5
# AttributeError: can't set attribute
@module_property
มัณฑนากรนี้ โดยทั่วไป@property
มัณฑนากรในตัวจะใช้เมื่อมีการกำหนดคลาสไม่ใช่หลังจากสร้างอินสแตนซ์แล้วดังนั้นฉันคิดว่าสิ่งเดียวกันนี้จะเป็นจริงสำหรับคุณสมบัติโมดูลและเป็นไปตามคำตอบของอเล็กซ์ - จำคำถามนี้ได้ "โมดูลสามารถมีคุณสมบัติแบบเดียวกับออบเจ็กต์ได้หรือไม่" แต่มันเป็นไปได้ที่จะเพิ่มพวกเขาต่อไปและผมได้แก้ไขก่อนหน้านี้ของฉันข้อมูลโค้ดแสดงให้เห็นถึงวิธีการที่สามารถทำได้
cached_module_property
ความจริงที่__getattr__()
จะไม่ถูกเรียกอีกต่อไปหากแอตทริบิวต์ได้รับการกำหนดนั้นมีประโยชน์ (คล้ายกับสิ่งที่functools.cached_property
ทำสำเร็จ)
__getattr__
โมดูลสำหรับโซลูชันที่ทันสมัยยิ่งขึ้น