الفصل الأساسي ل python الفئة المشتقة (أو المزيد من طريقة بيثوني لتوسيع الفصول الدراسية)

StackOverflow https://stackoverflow.com/questions/3464061

سؤال

أحتاج إلى تمديد حزمة 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top