الفصل الأساسي ل python الفئة المشتقة (أو المزيد من طريقة بيثوني لتوسيع الفصول الدراسية)
-
27-09-2019 - |
سؤال
أحتاج إلى تمديد حزمة NetworkX Python وإضافة بعض الطرق إلى Graph
الفصل لريدتي الخاصة
الطريقة التي فكرت بها في القيام بذلك هي ببساطة استخلاص فئة جديدة تقول NewGraph
, ، وإضافة الطرق المطلوبة.
ومع ذلك ، هناك العديد من الوظائف الأخرى في NetworkX التي تنشئ وإرجاعها Graph
الكائنات (على سبيل المثال إنشاء رسم بياني عشوائي). أنا الآن بحاجة إلى قلب هذه Graph
الكائنات في NewGraph
الكائنات حتى أتمكن من استخدام أساليب جديدة.
ما هي أفضل طريقة للقيام بذلك؟ أم يجب أن أتعامل مع المشكلة بطريقة مختلفة تمامًا؟
المحلول
إذا كنت فقط تضيف سلوكًا ، وليس اعتمادًا على قيم المثيلات الإضافية ، يمكنك تعيين الكائن __class__
:
from math import pi
class Circle(object):
def __init__(self, radius):
self.radius = radius
def area(self):
return pi * self.radius**2
class CirclePlus(Circle):
def diameter(self):
return self.radius*2
def circumference(self):
return self.radius*2*pi
c = Circle(10)
print c.radius
print c.area()
print repr(c)
c.__class__ = CirclePlus
print c.diameter()
print c.circumference()
print repr(c)
مطبوعات:
10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>
هذا أقرب ما إلى "الممثل" بقدر ما يمكنك الحصول عليه في بيثون ، ومثل الصب في C ، لا يجب القيام بذلك دون إعطاء الأمر بعض التفكير. لقد نشرت مثالًا محدودًا إلى حد ما ، ولكن إذا تمكنت من البقاء ضمن القيود (فقط أضف سلوكًا ، لا يوجد مثيل جديد ، فقد يساعد ذلك في معالجة مشكلتك.
نصائح أخرى
فيما يلي كيفية استبدال "سحري" استبدال فئة في وحدة نمطية مع فئة فرعية مصممة خصيصًا دون لمس الوحدة النمطية. إنه ليس سوى عدد قليل من الخطوط الإضافية من إجراء التصنيف الفرعي العادي ، وبالتالي يمنحك (تقريبًا) كل قوة ومرونة الفئات الفرعية كمكافأة. على سبيل المثال ، يتيح لك هذا إضافة سمات جديدة ، إذا كنت ترغب في ذلك.
import networkx as nx
class NewGraph(nx.Graph):
def __getattribute__(self, attr):
"This is just to show off, not needed"
print "getattribute %s" % (attr,)
return nx.Graph.__getattribute__(self, attr)
def __setattr__(self, attr, value):
"More showing off."
print " setattr %s = %r" % (attr, value)
return nx.Graph.__setattr__(self, attr, value)
def plot(self):
"A convenience method"
import matplotlib.pyplot as plt
nx.draw(self)
plt.show()
حتى الآن هذا تمامًا مثل التصنيف الفرعي العادي. الآن نحتاج إلى ربط هذه الفئة الفرعية في networkx
الوحدة النمطية بحيث تكون كل مثيل nx.Graph
ينتج عنه NewGraph
كائن بدلاً من ذلك. إليك ما يحدث عادة عند إنشاء مثيل nx.Graph
اعتراض مع nx.Graph()
1. nx.Graph.__new__(nx.Graph) is called 2. If the returned object is a subclass of nx.Graph, __init__ is called on the object 3. The object is returned as the instance
سنحل محل nx.Graph.__new__
وجعلها تعود NewGraph
في حين أن. في ذلك ، نسمي __new__
طريقة object
بدلا من ال __new__
طريقة NewGraph
, ، لأن هذا الأخير هو مجرد طريقة أخرى لاستدعاء الطريقة التي نستبدلها ، وبالتالي ستؤدي إلى عودة لا نهاية لها.
def __new__(cls):
if cls == nx.Graph:
return object.__new__(NewGraph)
return object.__new__(cls)
# We substitute the __new__ method of the nx.Graph class
# with our own.
nx.Graph.__new__ = staticmethod(__new__)
# Test if it works
graph = nx.generators.random_graphs.fast_gnp_random_graph(7, 0.6)
graph.plot()
في معظم الحالات ، هذا كل ما تحتاج إلى معرفته ، ولكن هناك مسكات واحدة. لدينا الغالب من __new__
الطريقة تؤثر فقط nx.Graph
, وليس فئاتها الفرعية. على سبيل المثال ، إذا اتصلت nx.gn_graph
, ، الذي يعيد مثيل nx.DiGraph
, ، لن يكون لها أي من ملحقاتنا الفاخرة. تحتاج إلى الفئة الفرعية لكل من الفئات الفرعية nx.Graph
التي ترغب في العمل معها وإضافة أساليب وسماتك المطلوبة. استخدام مزيج قد يجعل من الأسهل تمديد الفئات الفرعية باستمرار مع طاعة جاف المبدأ.
على الرغم من أن هذا المثال قد يبدو واضحًا بما فيه الكفاية ، إلا أن طريقة التثبيت في وحدة نمطية يصعب تعميمها بطريقة تغطي جميع المشكلات الصغيرة التي قد تظهر. أعتقد أنه من الأسهل تخصيصها للمشكلة المطروحة. على سبيل المثال ، إذا كان الفصل الذي تقوم فيه بالتوصيف فيه يحدد العرف الخاص به __new__
الطريقة ، تحتاج إلى تخزينها قبل استبدالها ، والاتصال بهذه الطريقة بدلاً من object.__new__
.
إذا كانت الوظيفة تقوم بإنشاء كائنات رسم بياني ، فلن تتمكن من تحويلها إلى كائنات NewGraph.
خيار آخر هو أن Newgraph هو الحصول على رسم بياني بدلاً من أن يكون رسمًا بيانيًا. تقوم بتفويض طرق الرسم البياني إلى كائن الرسم البياني لديك ، ويمكنك لف أي كائن رسم بياني في كائن NewGraph جديد:
class NewGraph:
def __init__(self, graph):
self.graph = graph
def some_graph_method(self, *args, **kwargs):
return self.graph.some_graph_method(*args, **kwargs)
#.. do this for the other Graph methods you need
def my_newgraph_method(self):
....
لحالتك البسيطة ، يمكنك أيضًا كتابة الفئة الفرعية الخاصة بك __init__
مثل هذا وتعيين المؤشرات من هياكل بيانات الرسم البياني إلى بيانات الفئة الفرعية الخاصة بك.
from networkx import Graph
class MyGraph(Graph):
def __init__(self, graph=None, **attr):
if graph is not None:
self.graph = graph.graph # graph attributes
self.node = graph.node # node attributes
self.adj = graph.adj # adjacency dict
else:
self.graph = {} # empty graph attr dict
self.node = {} # empty node attr dict
self.adj = {} # empty adjacency dict
self.edge = self.adj # alias
self.graph.update(attr) # update any command line attributes
if __name__=='__main__':
import networkx as nx
R=nx.gnp_random_graph(10,0.4)
G=MyGraph(R)
يمكنك أيضًا استخدام Copy () أو DeepCopy () في المهام ولكن إذا كنت تفعل ذلك ، فقد تستخدم أيضًا
G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())
لتحميل بيانات الرسم البياني الخاص بك.
يمكنك ببساطة إنشاء جديد NewGraph
مستمدة من Graph
الكائن ولديه __init__
تشمل الوظيفة شيئًا مثل self.__dict__.update(vars(incoming_graph))
كخط أول ، قبل أن تحدد خصائصك الخاصة. وبهذه الطريقة تقوم أساسًا بنسخ جميع الخصائص من Graph
لديك على كائن جديد ، مشتق من Graph
, ، ولكن مع صلصة الخاصة بك.
class NewGraph(Graph):
def __init__(self, incoming_graph):
self.__dict__.update(vars(incoming_graph))
# rest of my __init__ code, including properties and such
الاستخدام:
graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)
هل حاولتم يا رفاقPython] CAST BASE CLASS إلى فئة مشتقة
لقد اختبرته ، ويبدو أنه يعمل. أعتقد أيضًا أن هذه الطريقة أفضل قليلاً من واحدة أدناه لأن واحد لا يتم تنفيذه فيه وظيفة الوظيفة المشتقة.
c.__class__ = CirclePlus