পাইথনের বেস ক্লাস থেকে উত্তরাধিকারসূত্রে প্রাপ্ত সমস্ত ক্লাস পাওয়ার জন্য আমার একটি কার্যকরী পদ্ধতির প্রয়োজন।
পাইথনের বেস ক্লাস থেকে উত্তরাধিকারসূত্রে প্রাপ্ত সমস্ত ক্লাস পাওয়ার জন্য আমার একটি কার্যকরী পদ্ধতির প্রয়োজন।
উত্তর:
নতুন-স্টাইলের ক্লাসগুলি (অর্থাত্ উপক্লাসেড object
, যা পাইথন 3 এ ডিফল্ট) এর একটি __subclasses__
পদ্ধতি রয়েছে যা উপশ্রেনীর ফেরত দেয়:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
সাবক্লাসের নাম এখানে:
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
এখানে সাবক্লাসগুলি নিজেরাই রয়েছে:
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
নিশ্চিতকরণ যে সাবক্লাসগুলি প্রকৃতপক্ষে Foo
তাদের বেস হিসাবে তালিকাবদ্ধ করে:
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
দ্রষ্টব্য আপনি যদি সাবস্ক্রাক্লাস চান তবে আপনার পুনরাবৃত্তি করতে হবে:
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}
মনে রাখবেন যে যদি সাবক্লাসের শ্রেণি সংজ্ঞাটি এখনও কার্যকর না করা হয় - উদাহরণস্বরূপ, যদি সাবক্লাসের মডিউলটি এখনও আমদানি করা হয়নি - তবে সেই সাবক্লাসটি এখনও বিদ্যমান নেই এবং __subclasses__
এটি খুঁজে পাবে না।
আপনি "এর নাম দেওয়া হয়েছে" উল্লেখ করেছেন। যেহেতু পাইথন ক্লাসগুলি প্রথম শ্রেণীর অবজেক্ট, তাই আপনার ক্লাসের নামের সাথে কোনও স্ট্রিং বা শ্রেণীর মতো কিছু ব্যবহার করার দরকার নেই। আপনি কেবল ক্লাসটি সরাসরি ব্যবহার করতে পারেন এবং আপনার সম্ভবত হওয়া উচিত।
যদি আপনার কাছে কোনও শ্রেণীর নাম উপস্থাপন করে এমন একটি স্ট্রিং থাকে এবং আপনি সেই শ্রেণীর সাবক্লাসগুলি সন্ধান করতে চান তবে দুটি ধাপ রয়েছে: শ্রেণীর নাম দেওয়া আছে এবং তারপরে __subclasses__
উপরের মতো সাবক্লাসগুলি সন্ধান করুন ।
নাম থেকে শ্রেণিটি কীভাবে খুঁজে পাওয়া যায় তার উপর নির্ভর করে আপনি এটি কোথায় পাবেন। আপনি যদি ক্লাসটি সনাক্ত করার চেষ্টা করছেন এমন কোডের মতো একই মডিউলে এটি সন্ধান করার প্রত্যাশা করছেন, তবে
cls = globals()[name]
কাজটি করবে, বা আপনি স্থানীয়দের মধ্যে এটি খুঁজে পাওয়ার প্রত্যাশিত ক্ষেত্রে,
cls = locals()[name]
যদি শ্রেণিটি কোনও মডিউলে থাকতে পারে, তবে আপনার নামের স্ট্রিংয়ে পুরো-যোগ্যতাসম্পন্ন নাম থাকতে হবে - ন্যায়বিচারের 'pkg.module.Foo'
পরিবর্তে এমন কিছু 'Foo'
। importlib
শ্রেণীর মডিউলটি লোড করতে ব্যবহার করুন , তারপরে সংশ্লিষ্ট বৈশিষ্ট্যটি পুনরুদ্ধার করুন:
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
তবে আপনি ক্লাসটি সন্ধান করেন, cls.__subclasses__()
তারপরে তার সাবক্লাসগুলির একটি তালিকা ফিরিয়ে আনবেন।
আপনি যদি কেবল সরাসরি সাবক্লাস চান তবে ঠিকঠাক .__subclasses__()
কাজ করে। আপনি যদি সমস্ত সাবক্লাস, সাবক্লাসের সাবক্লাস এবং আরও কিছু চান, আপনার জন্য এটি করার জন্য আপনার একটি ফাংশন প্রয়োজন।
এখানে একটি সাধারণ, পঠনযোগ্য ফাংশন যা পুনরাবৃত্তভাবে একটি প্রদত্ত শ্রেণীর সমস্ত সাবক্লাসটি খুঁজে পায়:
def get_all_subclasses(cls):
all_subclasses = []
for subclass in cls.__subclasses__():
all_subclasses.append(subclass)
all_subclasses.extend(get_all_subclasses(subclass))
return all_subclasses
all_subclasses
একটি হওয়া উচিত নয় set
?
A(object)
, B(A)
, C(A)
, এবং D(B, C)
। get_all_subclasses(A) == [B, C, D, D]
।
সাধারণ আকারে সবচেয়ে সহজ সমাধান:
def get_subclasses(cls):
for subclass in cls.__subclasses__():
yield from get_subclasses(subclass)
yield subclass
এবং আপনার যদি একক শ্রেণি রয়েছে যেখানে আপনি উত্তরাধিকারসূত্রে উত্তীর্ণ হন সে ক্ষেত্রে একটি শ্রেণীবদ্ধ:
@classmethod
def get_subclasses(cls):
for subclass in cls.__subclasses__():
yield from subclass.get_subclasses()
yield subclass
__init_subclass__
অন্যান্য উত্তর হিসাবে উল্লিখিত হিসাবে আপনি __subclasses__
উপশ্রেণীর তালিকা পেতে বৈশিষ্ট্যটি পরীক্ষা করতে পারেন , পাইথন ৩.6 যেহেতু আপনি __init_subclass__
পদ্ধতিটি ওভাররাইড করে এই বৈশিষ্ট্যটি তৈরি করতে সংশোধন করতে পারেন ।
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
এইভাবে, আপনি কী করছেন তা যদি আপনি জানেন তবে আপনি __subclasses__
এই তালিকা থেকে সাবক্লাসের আচরণ ও বাদ দিতে / যুক্ত করতে পারেন ।
__init_subclass
পিতামাতার ক্লাসে ট্রিগার করবে ।
দ্রষ্টব্য: আমি দেখতে পাচ্ছি যে কেউ (@unutbu নয়) রেফারেন্স করা উত্তরটি পরিবর্তন করেছে যাতে এটি আর ব্যবহার না করে vars()['Foo']
- সুতরাং আমার পোস্টের প্রাথমিক বিন্দু আর প্রযোজ্য নয়।
এফডব্লিউআইডাব্লু, এখানে কেবলমাত্র স্থানীয়ভাবে সংজ্ঞায়িত শ্রেণীর সাথে কাজ করা @ আনটবুর উত্তর সম্পর্কে আমি কী বোঝাতে চাইছিলাম - এবং এর eval()
পরিবর্তে এটি ব্যবহার করা vars()
কেবলমাত্র বর্তমান স্কোপে সংজ্ঞায়িত নয় কেবল কোনও অ্যাক্সেসযোগ্য শ্রেণীর সাথে কাজ করবে।
যারা ব্যবহার পছন্দ করেন eval()
না তাদের পক্ষে এটি এড়াতে একটি উপায়ও দেখানো হয়েছে।
প্রথমে এখানে একটি শক্ত উদাহরণ ব্যবহার করে সম্ভাব্য সমস্যাটি দেখায় vars()
:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
# unutbu's approach
def all_subclasses(cls):
return cls.__subclasses__() + [g for s in cls.__subclasses__()
for g in all_subclasses(s)]
print(all_subclasses(vars()['Foo'])) # Fine because Foo is in scope
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
def func(): # won't work because Foo class is not locally defined
print(all_subclasses(vars()['Foo']))
try:
func() # not OK because Foo is not local to func()
except Exception as e:
print('calling func() raised exception: {!r}'.format(e))
# -> calling func() raised exception: KeyError('Foo',)
print(all_subclasses(eval('Foo'))) # OK
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
# using eval('xxx') instead of vars()['xxx']
def func2():
print(all_subclasses(eval('Foo')))
func2() # Works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
এই চলমান দ্বারা উন্নত করা যেতে পারে eval('ClassName')
অর্জন অতিরিক্ত সাধারণত্ব ক্ষতি ছাড়া সংজ্ঞায়িত ফাংশন মধ্যে নিচে, যা সহজে ব্যবহার তোলে ব্যবহার করে eval()
যা অসদৃশ vars()
প্রসঙ্গ-সংবেদী নয়:
# easier to use version
def all_subclasses2(classname):
direct_subclasses = eval(classname).__subclasses__()
return direct_subclasses + [g for s in direct_subclasses
for g in all_subclasses2(s.__name__)]
# pass 'xxx' instead of eval('xxx')
def func_ez():
print(all_subclasses2('Foo')) # simpler
func_ez()
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
শেষ অবধি, এটি eval()
সুরক্ষার কারণে ব্যবহার এড়াতে কিছুটা ক্ষেত্রে সম্ভব এবং সম্ভবত এটি গুরুত্বপূর্ণ , সুতরাং এটি ছাড়া এটি এখানে একটি সংস্করণ রয়েছে:
def get_all_subclasses(cls):
""" Generator of all a class's subclasses. """
try:
for subclass in cls.__subclasses__():
yield subclass
for subclass in get_all_subclasses(subclass):
yield subclass
except TypeError:
return
def all_subclasses3(classname):
for cls in get_all_subclasses(object): # object is base of all new-style classes.
if cls.__name__.split('.')[-1] == classname:
break
else:
raise ValueError('class %s not found' % classname)
direct_subclasses = cls.__subclasses__()
return direct_subclasses + [g for s in direct_subclasses
for g in all_subclasses3(s.__name__)]
# no eval('xxx')
def func3():
print(all_subclasses3('Foo'))
func3() # Also works
# -> [<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>]
eval()
- এখন আরও ভাল?
সমস্ত উপশ্রেণীর তালিকা পাওয়ার জন্য একটি সংক্ষিপ্ত সংস্করণ:
from itertools import chain
def subclasses(cls):
return list(
chain.from_iterable(
[list(chain.from_iterable([[x], subclasses(x)])) for x in cls.__subclasses__()]
)
)
শ্রেণীর সমস্ত সাবক্লাসের নাম দেওয়া আছে কীভাবে তা আমি খুঁজে পেতে পারি?
হ্যাঁ, আমরা অবশ্যই সহজেই অবজেক্টটিতে এই প্রদত্ত অ্যাক্সেসটি করতে পারি।
কেবল একই নাম দেওয়া একটি দুর্বল ধারণা, কারণ একই নামের একাধিক শ্রেণি এমনকি এমনকি একই মডিউলে সংজ্ঞায়িত হতে পারে।
আমি অন্য উত্তরের জন্য একটি বাস্তবায়ন তৈরি করেছি , এবং যেহেতু এটি এই প্রশ্নের উত্তর দেয় এবং এটি অন্য সমাধানগুলির চেয়ে এখানে আরও মার্জিত, তাই এখানে:
def get_subclasses(cls):
"""returns all subclasses of argument, cls"""
if issubclass(cls, type):
subclasses = cls.__subclasses__(cls)
else:
subclasses = cls.__subclasses__()
for subclass in subclasses:
subclasses.extend(get_subclasses(subclass))
return subclasses
ব্যবহার:
>>> import pprint
>>> list_of_classes = get_subclasses(int)
>>> pprint.pprint(list_of_classes)
[<class 'bool'>,
<enum 'IntEnum'>,
<enum 'IntFlag'>,
<class 'sre_constants._NamedIntConstant'>,
<class 'subprocess.Handle'>,
<enum '_ParameterKind'>,
<enum 'Signals'>,
<enum 'Handlers'>,
<enum 'RegexFlag'>]
এটি বিশেষ বিল্ট-ইন __subclasses__()
ক্লাস পদ্ধতিটি ব্যবহার করার মতো উত্তম উত্তর নয় যা @ ইউন্টবুবু উল্লেখ করেছেন, তাই আমি এটিকে নিছক অনুশীলন হিসাবে উপস্থাপন করি। subclasses()
ম ফাংশন পূর্বনির্ধারণ আয় একটি অভিধান যা উপশ্রেণী নিজেদের সব উপশ্রেণী নাম মানচিত্র তৈরী করে।
def traced_subclass(baseclass):
class _SubclassTracer(type):
def __new__(cls, classname, bases, classdict):
obj = type(classname, bases, classdict)
if baseclass in bases: # sanity check
attrname = '_%s__derived' % baseclass.__name__
derived = getattr(baseclass, attrname, {})
derived.update( {classname:obj} )
setattr(baseclass, attrname, derived)
return obj
return _SubclassTracer
def subclasses(baseclass):
attrname = '_%s__derived' % baseclass.__name__
return getattr(baseclass, attrname, None)
class BaseClass(object):
pass
class SubclassA(BaseClass):
__metaclass__ = traced_subclass(BaseClass)
class SubclassB(BaseClass):
__metaclass__ = traced_subclass(BaseClass)
print subclasses(BaseClass)
আউটপুট:
{'SubclassB': <class '__main__.SubclassB'>,
'SubclassA': <class '__main__.SubclassA'>}
পুনরাবৃত্তি ছাড়াই এখানে একটি সংস্করণ রয়েছে:
def get_subclasses_gen(cls):
def _subclasses(classes, seen):
while True:
subclasses = sum((x.__subclasses__() for x in classes), [])
yield from classes
yield from seen
found = []
if not subclasses:
return
classes = subclasses
seen = found
return _subclasses([cls], [])
এটি অন্যান্য বাস্তবায়নের থেকে পৃথক যে এটি আসল শ্রেণিকে দেয়। কারণ কোডটি সহজতর করে এবং:
class Ham(object):
pass
assert(issubclass(Ham, Ham)) # True
যদি get_subclasses_gen কিছুটা অদ্ভুত লাগে তবে এটি লেজ-পুনরাবৃত্তির বাস্তবায়নকে একটি লুপিং জেনারেটরে রূপান্তর করে তৈরি করা হয়েছিল:
def get_subclasses(cls):
def _subclasses(classes, seen):
subclasses = sum(*(frozenset(x.__subclasses__()) for x in classes))
found = classes + seen
if not subclasses:
return found
return _subclasses(subclasses, found)
return _subclasses([cls], [])