पायथन फ़ंक्शन ओवरलोडिंग


213

मुझे पता है कि पाइथन ओवरलोडिंग के तरीके का समर्थन नहीं करता है, लेकिन मैं एक ऐसी समस्या में चला गया हूं जिसे मैं एक अच्छा पाइकोनिक तरीके से हल नहीं कर सकता।

मैं एक ऐसा खेल बना रहा हूं, जहां एक चरित्र को विभिन्न प्रकार की गोलियों की शूटिंग करने की आवश्यकता होती है, लेकिन मैं इन गोलियों को बनाने के लिए विभिन्न कार्यों को कैसे लिखूं? उदाहरण के लिए मान लीजिए कि मेरे पास एक फंक्शन है जो एक गति से बिंदु A से B तक यात्रा करने वाली बुलेट बनाता है। मैं इस तरह से एक फ़ंक्शन लिखूंगा:

    def add_bullet(sprite, start, headto, speed):
        ... Code ...

लेकिन मैं बुलेट बनाने के लिए अन्य कार्य लिखना चाहता हूं जैसे:

    def add_bullet(sprite, start, direction, speed):
    def add_bullet(sprite, start, headto, spead, acceleration):
    def add_bullet(sprite, script): # For bullets that are controlled by a script
    def add_bullet(sprite, curve, speed): # for bullets with curved paths
    ... And so on ...

और बहुत सारे बदलावों के साथ। क्या इतने सारे खोजशब्द तर्कों का उपयोग किए बिना इसे करने का एक बेहतर तरीका है, इसके थोड़े बदसूरत होने का कारण है। प्रत्येक कार्य का नाम बदल रहा है क्योंकि आप या तो मिल भी बहुत बुरा है add_bullet1, add_bullet2या add_bullet_with_really_long_name

कुछ उत्तरों को संबोधित करने के लिए:

  1. नहीं, मैं एक बुलेट क्लास पदानुक्रम नहीं बना सकता क्योंकि यह बहुत धीमा है। गोलियों के प्रबंधन के लिए वास्तविक कोड C में है और मेरे कार्य C API के आसपास के रैपर हैं।

  2. मुझे पता है कि कीवर्ड तर्कों के बारे में है, लेकिन सभी प्रकार के मापदंडों के संयोजन के लिए जाँच करना कष्टप्रद हो रहा है, लेकिन डिफ़ॉल्ट तर्क जैसे आवंटन में मदद करते हैं acceleration=0


5
केवल एक पैरामीटर के लिए काम करता है, लेकिन यहां (एक खोज इंजन से यहां आने वाले लोगों के लिए): docs.python.org/3/library/…
leewz

1
यह डिफ़ॉल्ट मानों के लिए एक अच्छी जगह की तरह लगता है। आप कुछ को भी सेट कर सकते हैं और सिर्फ उनके लिए देख सकते हैं। अतिरिक्त बूलियन प्रभाव लापरवाह लगता है
एंड्रयू स्कॉट इवांस

default value + if + elseC ++ do के समान करने के लिए उपयोग करना होगा। यह उन कुछ चीजों में से एक है जो C ++ में पायथन की तुलना में बेहतर पठनीयता है ...
Deqing

मैं इस बात पर असमंजस में हूँ कि कौरव कोई मान्य उत्तर क्यों नहीं देते। आप कहते हैं कि आप कई कीवर्ड तर्क का उपयोग नहीं करना चाहते हैं क्योंकि यह बदसूरत हो जाता है ... अच्छी तरह से यह समस्या की प्रकृति है। यदि आपके पास कई तर्क हैं और यह गड़बड़ है क्योंकि आपके पास कई तर्क हैं जो आपने अपेक्षा की थी? क्या आप उन्हें कहीं भी निर्दिष्ट किए बिना कई तर्कों का उपयोग करना चाहते हैं ??? अजगर एक मन पाठक नहीं है।
कलन

हम नहीं जानते कि किस प्रकार की वस्तुएं script, curveहैं, क्या उनके पास एक सामान्य पूर्वज है, वे किन तरीकों का समर्थन करते हैं। बत्तख-टाइपिंग के साथ, यह वर्ग डिजाइन के लिए आपके ऊपर है कि वे किन तरीकों का समर्थन करें। संभवतया Scriptकिसी प्रकार के टाइमस्टेप-आधारित कॉलबैक का समर्थन करता है (लेकिन क्या वस्तु वापस आनी चाहिए? उस टाइमस्टैप पर स्थिति? उस टाइमस्टेप पर प्रक्षेपवक्र?)। संभवतया start, direction, speedऔर start, headto, spead, accelerationदोनों प्रकार के प्रक्षेपवक्रों का वर्णन करते हैं, लेकिन फिर से यह आप पर निर्भर है कि वे किस तरह से उन्हें खोलना और उन्हें संसाधित करना जानते हैं।
स्माइली

जवाबों:


144

आप जो मांग रहे हैं, उसे कई प्रेषण कहा जाता है । जूलिया भाषा के उदाहरण देखें जो विभिन्न प्रकार के प्रेषण प्रदर्शित करता है।

हालाँकि, उस पर देखने से पहले, हम पहले बताएंगे कि वास्तव में ओवरलोडिंग क्यों नहीं है जो आप अजगर में चाहते हैं।

ओवरलोडिंग क्यों नहीं?

सबसे पहले, एक को ओवरलोडिंग की अवधारणा को समझने की आवश्यकता है और यह अजगर पर लागू क्यों नहीं है।

जब भाषाओं के साथ काम करना जो संकलन-समय पर डेटा प्रकारों में भेदभाव कर सकता है, तो विकल्पों में से चयन करना संकलन-समय पर हो सकता है। संकलन-समय चयन के लिए ऐसे वैकल्पिक कार्य बनाने का कार्य आमतौर पर एक फ़ंक्शन को ओवरलोड करने के रूप में संदर्भित किया जाता है। ( विकिपीडिया )

पायथन एक गतिशील रूप से टाइप की जाने वाली भाषा है, इसलिए ओवरलोडिंग की अवधारणा बस इस पर लागू नहीं होती है। हालाँकि, सभी नष्ट नहीं हुए हैं, क्योंकि हम रन-टाइम पर ऐसे वैकल्पिक कार्य बना सकते हैं :

प्रोग्रामिंग भाषाओं में जो रन-टाइम तक डेटा प्रकार की पहचान को स्थगित करते हैं, गतिशील रूप से निर्धारित प्रकार के फ़ंक्शन तर्कों के आधार पर वैकल्पिक कार्यों के बीच चयन होना चाहिए। ऐसे कार्य जिनके वैकल्पिक कार्यान्वयन को इस तरीके से चुना जाता है, उन्हें आमतौर पर मल्टीमिथोड के रूप में संदर्भित किया जाता है । ( विकिपीडिया )

इसलिए हमें अजगर में मल्टीमिथोड्स करने में सक्षम होना चाहिए - या, जैसा कि इसे वैकल्पिक रूप से कहा जाता है: एकाधिक प्रेषण

बहु प्रेषण

मल्टीमिथोड को कई प्रेषण भी कहा जाता है :

मल्टीपल डिस्पैच या मल्टीमिथोड्स कुछ ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग लैंग्वेज की विशेषता है, जिसमें एक फ़ंक्शन या विधि को उसके एक से अधिक तर्कों के रन टाइम (गतिशील) प्रकार के आधार पर गतिशील रूप से भेजा जा सकता है। ( विकिपीडिया )

अजगर 1 बॉक्स से बाहर इस का समर्थन नहीं करता है , लेकिन जैसा कि होता है, एक उत्कृष्ट अजगर पैकेज है जिसे मल्टीप्लास्पाइच कहा जाता है जो वास्तव में ऐसा करता है।

उपाय

यहां बताया गया है कि हम आपके तरीकों को लागू करने के लिए मल्टीप्लेस्पेड 2 पैकेज का उपयोग कैसे कर सकते हैं :

>>> from multipledispatch import dispatch
>>> from collections import namedtuple  
>>> from types import *  # we can test for lambda type, e.g.:
>>> type(lambda a: 1) == LambdaType
True

>>> Sprite = namedtuple('Sprite', ['name'])
>>> Point = namedtuple('Point', ['x', 'y'])
>>> Curve = namedtuple('Curve', ['x', 'y', 'z'])
>>> Vector = namedtuple('Vector', ['x','y','z'])

>>> @dispatch(Sprite, Point, Vector, int)
... def add_bullet(sprite, start, direction, speed):
...     print("Called Version 1")
...
>>> @dispatch(Sprite, Point, Point, int, float)
... def add_bullet(sprite, start, headto, speed, acceleration):
...     print("Called version 2")
...
>>> @dispatch(Sprite, LambdaType)
... def add_bullet(sprite, script):
...     print("Called version 3")
...
>>> @dispatch(Sprite, Curve, int)
... def add_bullet(sprite, curve, speed):
...     print("Called version 4")
...

>>> sprite = Sprite('Turtle')
>>> start = Point(1,2)
>>> direction = Vector(1,1,1)
>>> speed = 100 #km/h
>>> acceleration = 5.0 #m/s
>>> script = lambda sprite: sprite.x * 2
>>> curve = Curve(3, 1, 4)
>>> headto = Point(100, 100) # somewhere far away

>>> add_bullet(sprite, start, direction, speed)
Called Version 1

>>> add_bullet(sprite, start, headto, speed, acceleration)
Called version 2

>>> add_bullet(sprite, script)
Called version 3

>>> add_bullet(sprite, curve, speed)
Called version 4

1. पायथन 3 वर्तमान में एकल प्रेषण का समर्थन करता है
। 2. ध्यान रखें कि बहु-थ्रेडेड वातावरण में मल्टीप्लेडस्पाच का उपयोग न करें या आपको अजीब व्यवहार मिलेगा।


6
मल्टी-थ्रेडेड वातावरण में 'मल्टीप्लेस्पाइस्ट' के साथ समस्या क्या है? चूंकि सर्वर-साइड का कोड आमतौर पर बहु-थ्रेडेड वातावरण में होता है! बस इसे खोदने की कोशिश कर रहा है!
दनजीर

7
@danzeer यह थ्रेड-सुरक्षित नहीं था। मैंने तर्क को दो अलग-अलग थ्रेड द्वारा संशोधित किया जा रहा है (यानी speedफ़ंक्शन के बीच में मूल्य बदल सकता है जब एक और थ्रेड सेट होता है, तो यह स्वयं का मूल्य है speed) !!! मुझे यह महसूस करने में बहुत समय लगा कि यह वह पुस्तकालय था जो अपराधी था।
एंड्री Drozdyuk

108

जैसा कि आप इसे पेश करते हैं, पायथन "मेथड ओवरलोडिंग" का समर्थन करता है। वास्तव में, जो आप अभी वर्णन करते हैं, वह इतने अलग-अलग तरीकों से अजगर में लागू करने के लिए तुच्छ है, लेकिन मैं इसके साथ जाऊंगा:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, sprite=default, start=default, 
                 direction=default, speed=default, accel=default, 
                  curve=default):
        # do stuff with your arguments

उपरोक्त कोड में, defaultउन तर्कों के लिए एक प्रशंसनीय डिफ़ॉल्ट मान है, या None। आप तब विधि को केवल उन तर्कों के साथ कॉल कर सकते हैं जिनमें आप रुचि रखते हैं और पायथन डिफ़ॉल्ट मानों का उपयोग करेगा।

आप भी कुछ ऐसा कर सकते हैं:

class Character(object):
    # your character __init__ and other methods go here

    def add_bullet(self, **kwargs):
        # here you can unpack kwargs as (key, values) and
        # do stuff with them, and use some global dictionary
        # to provide default values and ensure that ``key``
        # is a valid argument...

        # do stuff with your arguments

एक अन्य विकल्प सीधे इच्छित फ़ंक्शन को सीधे वर्ग या उदाहरण के लिए हुक करना है:

def some_implementation(self, arg1, arg2, arg3):
  # implementation
my_class.add_bullet = some_implementation_of_add_bullet

अभी तक एक और तरीका एक अमूर्त कारखाना पैटर्न का उपयोग करना है:

class Character(object):
   def __init__(self, bfactory, *args, **kwargs):
       self.bfactory = bfactory
   def add_bullet(self):
       sprite = self.bfactory.sprite()
       speed = self.bfactory.speed()
       # do stuff with your sprite and speed

class pretty_and_fast_factory(object):
    def sprite(self):
       return pretty_sprite
    def speed(self):
       return 10000000000.0

my_character = Character(pretty_and_fast_factory(), a1, a2, kw1=v1, kw2=v2)
my_character.add_bullet() # uses pretty_and_fast_factory

# now, if you have another factory called "ugly_and_slow_factory" 
# you can change it at runtime in python by issuing
my_character.bfactory = ugly_and_slow_factory()

# In the last example you can see abstract factory and "method
# overloading" (as you call it) in action 

107
ये सभी ओवरलोडिंग के बजाय चर तर्कों के उदाहरण के रूप में दिखते हैं। चूंकि ओवरलोडिंग आपको विभिन्न प्रकार के तर्कों के लिए एक ही फ़ंक्शन करने की अनुमति देता है। उदाहरण: sum (real_num1, real_num2) और sum (काल्पनिक_num1, imaginary_num2) दोनों में एक ही कॉलिंग सिंटैक्स होगा, लेकिन वास्तव में इनपुट के रूप में 2 अलग-अलग प्रकार की उम्मीद कर रहे हैं, और कार्यान्वयन को आंतरिक रूप से भी बदलना है
Efren

17
आपके द्वारा दिए गए उत्तर का उपयोग करने पर, आप उस कॉल करने वाले को कैसे प्रस्तुत करेंगे जो तर्क एक साथ समझ में आते हैं? बस एक डिफ़ॉल्ट मान के साथ तर्कों का एक गुच्छा लगाने से एक ही कार्यक्षमता मिल सकती है लेकिन एक एपीआई के संदर्भ में यह बहुत कम सुरुचिपूर्ण है
ग्रेग एनिस

6
उपरोक्त में से कोई भी ओवरलोडिंग नहीं है, कार्यान्वयन को सभी मापदंडों के इनपुट (या मापदंडों को अनदेखा करना) की जांच करनी होगी जैसे: if sprite and script and not start and not direction and not speed...यह जानने के लिए कि यह एक विशिष्ट कार्रवाई में है। क्योंकि एक कॉलर उपलब्ध सभी मापदंडों को प्रदान करने वाले फ़ंक्शन को कॉल कर सकता है। ओवरलोडिंग आपके लिए प्रासंगिक मापदंडों के सटीक सेट को परिभाषित करता है।
रोई गैवरिएल

5
यह बहुत परेशान करने वाला है जब लोग कहते हैं कि अजगर ओवरलोडिंग की विधि का समर्थन करता है। ऐसा नहीं होता। तथ्य यह है कि आप उद्धरणों में "विधि अधिभार" डालते हैं, यह इंगित करता है कि आप इस तथ्य से अवगत हैं। आप कई तकनीकों के साथ समान कार्यक्षमता प्राप्त कर सकते हैं, जैसे कि यहां उल्लेख किया गया है। लेकिन विधि अधिभार की एक बहुत विशिष्ट परिभाषा है।
हॉवर्ड स्वोप

मुझे लगता है कि अभिप्रेत बिंदु है जबकि विधि अतिभार एक अजगर की विशेषता नहीं है, उपरोक्त तंत्र का उपयोग समकक्ष प्रभाव को प्राप्त करने के लिए किया जा सकता है।
रॉरर

93

आप फ़ंक्शन ओवरलोडिंग के लिए "रोल-योर-ओन" समाधान का उपयोग कर सकते हैं। यह एक Guido van Rossum के मल्टीमिथोड्स के लेख से कॉपी किया गया है (क्योंकि अजगर में मिमी और ओवरलोडिंग के बीच बहुत कम अंतर है):

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function


def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = registry.get(name)
        if mm is None:
            mm = registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

उपयोग होगा

from multimethods import multimethod
import unittest

# 'overload' makes more sense in this case
overload = multimethod

class Sprite(object):
    pass

class Point(object):
    pass

class Curve(object):
    pass

@overload(Sprite, Point, Direction, int)
def add_bullet(sprite, start, direction, speed):
    # ...

@overload(Sprite, Point, Point, int, int)
def add_bullet(sprite, start, headto, speed, acceleration):
    # ...

@overload(Sprite, str)
def add_bullet(sprite, script):
    # ...

@overload(Sprite, Curve, speed)
def add_bullet(sprite, curve, speed):
    # ...

इस समय अधिकांश प्रतिबंधात्मक सीमाएँ हैं:

  • तरीकों का समर्थन नहीं किया जाता है, केवल ऐसे कार्य जो कक्षा के सदस्य नहीं हैं;
  • विरासत संभाला नहीं है;
  • kwargs समर्थित नहीं हैं;
  • नए कार्यों को पंजीकृत करना आयात समय पर किया जाना चाहिए बात थ्रेड-सुरक्षित नहीं है

6
इस उपयोग के मामले में भाषा का विस्तार करने के लिए सज्जाकारों के लिए +1।
एलोइम्स

1
+1 क्योंकि यह एक महान विचार है (और शायद ओपी के साथ क्या होना चाहिए) --- मैंने पायथन में कभी मल्टीमिथोड कार्यान्वयन नहीं देखा था।
Escualo

39

बहुविकल्पी मॉड्यूल का उपयोग करने के लिए एक संभावित विकल्प यहां विस्तृत है: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch

ऐसा करने के बजाय:

def add(self, other):
    if isinstance(other, Foo):
        ...
    elif isinstance(other, Bar):
        ...
    else:
        raise NotImplementedError()

तुम यह केर सकते हो:

from multipledispatch import dispatch
@dispatch(int, int)
def add(x, y):
    return x + y    

@dispatch(object, object)
def add(x, y):
    return "%s + %s" % (x, y)

परिणामी उपयोग के साथ:

>>> add(1, 2)
3

>>> add(1, 'hello')
'1 + hello'

4
इसे अधिक वोट क्यों नहीं मिलते? मैं उदाहरणों की कमी के कारण अनुमान लगा रहा हूँ ... मैंने एक उदाहरण के साथ एक उत्तर बनाया है कि ओपी की समस्या को बहुउद्देशीय पैकेज के साथ कैसे हल किया जाए ।
एंड्री डॉरजिदुक

19

पायथन 3.4 में PEP-0443 जोड़ा गया था एकल-प्रेषण सामान्य कार्य

यहाँ PEP से छोटा एपीआई विवरण है।

एक जेनेरिक फ़ंक्शन को परिभाषित करने के लिए, इसे @singledispatch डेकोरेटर से सजाएं। ध्यान दें कि प्रेषण पहले तर्क के प्रकार पर होता है। तदनुसार अपना कार्य बनाएं:

from functools import singledispatch
@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

फ़ंक्शन में अतिभारित कार्यान्वयन को जोड़ने के लिए, जेनेरिक फ़ंक्शन के रजिस्टर () विशेषता का उपयोग करें। यह एक डेकोरेटर है, एक प्रकार का पैरामीटर लेता है और उस प्रकार के लिए ऑपरेशन को लागू करने वाले फ़ंक्शन को सजाता है:

@fun.register(int)
def _(arg, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register(list)
def _(arg, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

11

इस प्रकार का व्यवहार आमतौर पर (OOP भाषाओं में) बहुरूपता का उपयोग करके हल किया जाता है। प्रत्येक प्रकार की गोली यह जानने के लिए जिम्मेदार होगी कि यह कैसे यात्रा करती है। उदाहरण के लिए:

class Bullet(object):
    def __init__(self):
        self.curve = None
        self.speed = None
        self.acceleration = None
        self.sprite_image = None

class RegularBullet(Bullet):
    def __init__(self):
        super(RegularBullet, self).__init__()
        self.speed = 10

class Grenade(Bullet):
    def __init__(self):
        super(Grenade, self).__init__()
        self.speed = 4
        self.curve = 3.5

add_bullet(Grendade())

def add_bullet(bullet):
    c_function(bullet.speed, bullet.curve, bullet.acceleration, bullet.sprite, bullet.x, bullet.y) 


void c_function(double speed, double curve, double accel, char[] sprite, ...) {
    if (speed != null && ...) regular_bullet(...)
    else if (...) curved_bullet(...)
    //..etc..
}

C_function के जितने भी तर्क मौजूद हैं, उन्हें पारित करें, फिर यह निर्धारित करने का काम करें कि प्रारंभिक c फ़ंक्शन में मानों के आधार पर किस सी फ़ंक्शन को कॉल किया जाए। तो, अजगर को केवल एक सी फ़ंक्शन को कॉल करना चाहिए। यह एक सी फ़ंक्शन तर्कों को देखता है, और फिर अन्य सी कार्यों को उचित रूप से सौंप सकता है।

आप अनिवार्य रूप से प्रत्येक उपवर्ग को एक अलग डेटा कंटेनर के रूप में उपयोग कर रहे हैं, लेकिन आधार वर्ग पर सभी संभावित तर्कों को परिभाषित करके, उपवर्ग उन लोगों की उपेक्षा करने के लिए स्वतंत्र हैं जिनके साथ वे कुछ भी नहीं करते हैं।

जब एक नई प्रकार की बुलेट आती है, तो आप बस आधार पर एक और संपत्ति को परिभाषित कर सकते हैं, एक अजगर फ़ंक्शन को बदल सकते हैं ताकि यह अतिरिक्त संपत्ति को पारित कर दे, और एक c_function जो तर्कों की जांच करता है और उचित रूप से प्रतिनिधि करता है। मुझे लगता है कि बहुत बुरा नहीं लगता।


1
यह मेरा प्रारंभिक दृष्टिकोण था, लेकिन प्रदर्शन कारणों से मुझे सी में उस कोड को फिर से लिखना पड़ा
बुलेट्स

@Bullets, मैं सुझाव दूंगा कि कई सारे c फ़ंक्शन लिखने के बजाय प्रदर्शन को बेहतर बनाने के लिए कई अलग-अलग विकल्प उपलब्ध हो सकते हैं, जो शायद पूरी तरह से नहीं होंगे। उदाहरण के लिए: एक उदाहरण बनाना महंगा हो सकता है, इसलिए ऑब्जेक्ट पूल बनाए रखें। हालांकि मैं यह जानता हूं कि आपको यह पता चले बिना कि आप बहुत धीमे हैं। रुचि से बाहर, वास्तव में इस दृष्टिकोण के बारे में क्या धीमा था? जब तक कि सीमा के सी साइड में महत्वपूर्ण समय व्यतीत नहीं हो रहा है, मैं नहीं सोच सकता कि पायथन (स्वयं) वास्तविक समस्या है।
जोश स्मेटन

हो सकता है कि प्रदर्शन को बेहतर बनाने के अन्य तरीके हों, लेकिन मैं पायथन के साथ सी की तुलना में बहुत बेहतर हूं। समस्या गोलियों की गति की गणना कर रही थी और स्क्रीन बाउंड से बाहर जाने पर पता लगाने की थी। मेरे पास गोली की स्थिति की गणना pos+v*tकरने और फिर स्क्रीन की सीमाओं if x > 800और इतने पर तुलना करने के लिए एक तरीके थे । इन कार्यों को प्रति फ्रेम कई सौ बार कॉल करना अस्वीकार्य रूप से धीमा था। यह कुछ ऐसा था जैसे कि 40% सीपीयू में 100% शुद्ध अजगर के साथ 60 एफपीएस पर 5% -10% के साथ जब सी। में किया जाता है
बुलेट्स

@ बुललेट्स, तब काफी फेयर। मैं अभी भी उस दृष्टिकोण का उपयोग करता हूं जो मैं डेटा को एनकैप्सुलेट करने के लिए गया था। बुलेट की एक आवृत्ति को पास करें add_bullet, और उन सभी फ़ील्ड्स को निकालें, जिनकी आपको ज़रूरत है। मैं अपना उत्तर संपादित करूँगा।
जोश स्मेटन

@Bullets: आप अपने सी कार्य करता है और OOP दृष्टिकोण जोश का उपयोग करके सुझाव गठजोड़ कर सकते हैं Cython । यह जल्दी बाध्यकारी की अनुमति देता है इसलिए गति दंड नहीं होना चाहिए।
JFS


4

या तो परिभाषा में कई कीवर्ड तर्कों का उपयोग करें, या एक Bulletपदानुक्रम बनाएं जिसका उदाहरण फ़ंक्शन के लिए दिया गया है।


मैं दूसरे दृष्टिकोण का सुझाव देने जा रहा था: बुलेट विवरण निर्दिष्ट करने के लिए कुछ BulletParams ... कक्षाएं बनाएं।
जॉन Zwinck

क्या आप इसे विस्तार से बताएंगे? मैंने विभिन्न गोलियों के साथ एक वर्ग पदानुक्रम बनाने की कोशिश की लेकिन यह काम नहीं करता है, क्योंकि पायथन बहुत धीमा है। यह गोलियों की आवश्यक संख्या की गति की तेज़ी से गणना नहीं कर सकता है, इसलिए मुझे सी में उस हिस्से को लिखना पड़ा। सभी add_bullet वेरिएंट बस इसी C फ़ंक्शन को कॉल करते हैं।
बुलेट्स

4

मुझे लगता है कि आपकी मूल आवश्यकता कम से कम सिरदर्द के साथ अजगर में सी / सी ++ जैसे सिंटैक्स की है। हालांकि मुझे अलेक्जेंडर पोलुटेकोव का जवाब पसंद आया क्योंकि यह कक्षाओं के लिए काम नहीं करता है।

कक्षाओं के लिए निम्नलिखित कार्य करना चाहिए। यह गैर कीवर्ड तर्कों की संख्या के आधार पर अलग-अलग काम करता है (लेकिन प्रकार द्वारा अंतर का समर्थन नहीं करता है):

class TestOverloading(object):
    def overloaded_function(self, *args, **kwargs):
        # Call the function that has the same number of non-keyword arguments.  
        getattr(self, "_overloaded_function_impl_" + str(len(args)))(*args, **kwargs)
    
    def _overloaded_function_impl_3(self, sprite, start, direction, **kwargs):
        print "This is overload 3"
        print "Sprite: %s" % str(sprite)
        print "Start: %s" % str(start)
        print "Direction: %s" % str(direction)
        
    def _overloaded_function_impl_2(self, sprite, script):
        print "This is overload 2"
        print "Sprite: %s" % str(sprite)
        print "Script: "
        print script

और इसका उपयोग इस तरह किया जा सकता है:

test = TestOverloading()

test.overloaded_function("I'm a Sprite", 0, "Right")
print
test.overloaded_function("I'm another Sprite", "while x == True: print 'hi'")

आउटपुट:

यह ओवरलोड है 3
स्प्राइट: आई एम ए स्प्राइट
स्टार्ट: 0
डायरेक्शन: राइट

यह ओवरलोड 2
स्प्राइट है: मैं एक और स्प्राइट
स्क्रिप्ट हूं :
जबकि x == सच: प्रिंट 'हाय'


4

@overloadडेकोरेटर प्रकार संकेत (पीईपी 484) के साथ जोड़ा गया है। हालांकि यह अजगर के व्यवहार को नहीं बदलता है, लेकिन यह समझने में आसान बनाता है कि क्या हो रहा है, और त्रुटियों का पता लगाने के लिए मैपी के लिए।
देखें: टाइप करें संकेत और पीईपी 484


क्या आप कुछ उदाहरण जोड़ सकते हैं?
Gerrit

3

मुझे लगता है Bulletकि संबद्ध बहुरूपता के साथ एक वर्ग पदानुक्रम जाने का रास्ता है। आप प्रभावी रूप से बेस क्लास कंस्ट्रक्टर को मेटाक्लास का उपयोग करके अधिभारित कर सकते हैं ताकि बेस क्लास के परिणाम को उचित उपवर्ग वस्तु के निर्माण में बुला सकें। नीचे कुछ नमूना कोड है जो मेरे मतलब के सार को स्पष्ट करने के लिए है।

अपडेट किया गया

कोड को प्रासंगिक बनाए रखने के लिए पायथन 2 और 3 दोनों के तहत चलने के लिए संशोधित किया गया है। यह एक तरह से किया गया था जो पायथन के स्पष्ट मेटाक्लस सिंटैक्स के उपयोग से बचा जाता है, जो दो संस्करणों के बीच भिन्न होता है।

उस उद्देश्य को पूरा करने के लिए, बेसकेल्स बनाते समय (मेटा क्लास विशेषता का उपयोग करने के बजाय या पायथन संस्करण के आधार पर एक खोजशब्द तर्क के माध्यम से) स्पष्ट रूप से मेटाक्लस को कॉल करके कक्षा का एक BulletMetaBaseउदाहरण BulletMetaबनाया जाता है ।Bullet__metaclass__=metaclass

class BulletMeta(type):
    def __new__(cls, classname, bases, classdict):
        """ Create Bullet class or a subclass of it. """
        classobj = type.__new__(cls, classname, bases, classdict)
        if classname != 'BulletMetaBase':
            if classname == 'Bullet':  # Base class definition?
                classobj.registry = {}  # Initialize subclass registry.
            else:
                try:
                    alias = classdict['alias']
                except KeyError:
                    raise TypeError("Bullet subclass %s has no 'alias'" %
                                    classname)
                if alias in Bullet.registry: # unique?
                    raise TypeError("Bullet subclass %s's alias attribute "
                                    "%r already in use" % (classname, alias))
                # Register subclass under the specified alias.
                classobj.registry[alias] = classobj

        return classobj

    def __call__(cls, alias, *args, **kwargs):
        """ Bullet subclasses instance factory.

            Subclasses should only be instantiated by calls to the base
            class with their subclass' alias as the first arg.
        """
        if cls != Bullet:
            raise TypeError("Bullet subclass %r objects should not to "
                            "be explicitly constructed." % cls.__name__)
        elif alias not in cls.registry: # Bullet subclass?
            raise NotImplementedError("Unknown Bullet subclass %r" %
                                      str(alias))
        # Create designated subclass object (call its __init__ method).
        subclass = cls.registry[alias]
        return type.__call__(subclass, *args, **kwargs)


class Bullet(BulletMeta('BulletMetaBase', (object,), {})):
    # Presumably you'd define some abstract methods that all here
    # that would be supported by all subclasses.
    # These definitions could just raise NotImplementedError() or
    # implement the functionality is some sub-optimal generic way.
    # For example:
    def fire(self, *args, **kwargs):
        raise NotImplementedError(self.__class__.__name__ + ".fire() method")

    # Abstract base class's __init__ should never be called.
    # If subclasses need to call super class's __init__() for some
    # reason then it would need to be implemented.
    def __init__(self, *args, **kwargs):
        raise NotImplementedError("Bullet is an abstract base class")


# Subclass definitions.
class Bullet1(Bullet):
    alias = 'B1'
    def __init__(self, sprite, start, direction, speed):
        print('creating %s object' % self.__class__.__name__)
    def fire(self, trajectory):
        print('Bullet1 object fired with %s trajectory' % trajectory)


class Bullet2(Bullet):
    alias = 'B2'
    def __init__(self, sprite, start, headto, spead, acceleration):
        print('creating %s object' % self.__class__.__name__)


class Bullet3(Bullet):
    alias = 'B3'
    def __init__(self, sprite, script): # script controlled bullets
        print('creating %s object' % self.__class__.__name__)


class Bullet4(Bullet):
    alias = 'B4'
    def __init__(self, sprite, curve, speed): # for bullets with curved paths
        print('creating %s object' % self.__class__.__name__)


class Sprite: pass
class Curve: pass

b1 = Bullet('B1', Sprite(), (10,20,30), 90, 600)
b2 = Bullet('B2', Sprite(), (-30,17,94), (1,-1,-1), 600, 10)
b3 = Bullet('B3', Sprite(), 'bullet42.script')
b4 = Bullet('B4', Sprite(), Curve(), 720)
b1.fire('uniform gravity')
b2.fire('uniform gravity')

आउटपुट:

creating Bullet1 object
creating Bullet2 object
creating Bullet3 object
creating Bullet4 object
Bullet1 object fired with uniform gravity trajectory
Traceback (most recent call last):
  File "python-function-overloading.py", line 93, in <module>
    b2.fire('uniform gravity') # NotImplementedError: Bullet2.fire() method
  File "python-function-overloading.py", line 49, in fire
    raise NotImplementedError(self.__class__.__name__ + ".fire() method")
NotImplementedError: Bullet2.fire() method

हम्म यह अभी भी add_bullet1, add_bullet2 और इतने पर के रूप में कार्यों को नाम देने के लिए सिर्फ एक फैंसी तरीका है।
बुलेट्स

1
@ बुललेट: शायद यह है, या शायद यह एक फैक्ट्री फ़ंक्शन बनाने के लिए थोड़ा विस्तृत तरीका है। इसके बारे में एक अच्छी बात यह है कि यह Bulletहर बार जब आप एक और उपप्रकार जोड़ते हैं तो आधार वर्ग या फैक्टरी फ़ंक्शन को संशोधित किए बिना उपवर्गों के पदानुक्रम का समर्थन करता है । (बेशक, यदि आप C ++ के बजाय C का उपयोग कर रहे हैं, तो मुझे लगता है कि आपके पास कक्षाएं नहीं हैं।) आप एक उत्कृष्ट मेटाक्लास भी बना सकते हैं जो अपने आप ही पता लगा ले कि किस प्रकार और / या संख्या के आधार पर उप-वर्ग बनाएं। पारित तर्कों (जैसे C ++ ओवरलोडिंग का समर्थन करता है)।
मार्टीन्यू

1
यह उत्तराधिकार विचार मेरा पहला विकल्प होगा।
डैनियल मोलर

3

पायथन 3.8 ने फंक्शनलूलसिंगस्लाइड्सइस्पेक्टेमेथोड जोड़ा

एकल-प्रेषण जेनेरिक फ़ंक्शन में एक विधि रूपांतरण।

एक सामान्य विधि को परिभाषित करने के लिए, इसे @singledispatchmethod डेकोरेटर से सजाएं। ध्यान दें कि प्रेषण पहले गैर-स्व या गैर-क्लेश तर्क के प्रकार पर होता है, तदनुसार अपना फ़ंक्शन बनाएं:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg


negator = Negator()
for v in [42, True, "Overloading"]:
    neg = negator.neg(v)
    print(f"{v=}, {neg=}")

उत्पादन

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

@singledispatchmethod @classmethod जैसे अन्य सज्जाकारों के साथ नेस्टिंग का समर्थन करता है। ध्यान दें कि dispatcher.register के लिए अनुमति देने के लिए, singledispatchmethod बाहरी सबसे डेकोरेटर होना चाहिए। यहाँ नकारात्मक वर्ग को बाध्य करने वाली नकारात्मक विधियों के साथ वर्ग है:

from functools import singledispatchmethod


class Negator:
    @singledispatchmethod
    @staticmethod
    def neg(arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(arg: int) -> int:
        return -arg

    @neg.register
    def _(arg: bool) -> bool:
        return not arg


for v in [42, True, "Overloading"]:
    neg = Negator.neg(v)
    print(f"{v=}, {neg=}")

आउटपुट:

v=42, neg=-42
v=True, neg=False
NotImplementedError: Cannot negate a

समान पैटर्न का उपयोग अन्य समान डेकोरेटर्स के लिए किया जा सकता है: स्टेटमिथोड, एब्सट्रैमेथोड और अन्य।


2

डिफ़ॉल्ट के साथ कीवर्ड तर्क का उपयोग करें। उदाहरण के लिए

def add_bullet(sprite, start=default, direction=default, script=default, speed=default):

सीधी गोली बनाम घुमावदार गोली के मामले में, मैं दो कार्य जोड़ूंगा: add_bullet_straightऔर add_bullet_curved


2

अजगर में ओवरलोडिंग के तरीके मुश्किल हैं। हालांकि, डिक्टेट, लिस्ट या आदिम वेरिएबल को पास करने का उपयोग हो सकता है।

मैंने अपने उपयोग के मामलों के लिए कुछ करने की कोशिश की है, इससे यहां लोगों को तरीकों को ओवरलोड करने के लिए समझने में मदद मिल सकती है।

चलो अपना उदाहरण लेते हैं:

एक वर्ग अधिभार विधि विभिन्न वर्ग से विधियों को बुलाती है।

def add_bullet(sprite=None, start=None, headto=None, spead=None, acceleration=None):

दूरस्थ वर्ग से तर्क पारित करें:

add_bullet(sprite = 'test', start=Yes,headto={'lat':10.6666,'long':10.6666},accelaration=10.6}

या

add_bullet(sprite = 'test', start=Yes, headto={'lat':10.6666,'long':10.6666},speed=['10','20,'30']}

इसलिए, विधि अधिभार से सूची, शब्दकोश या आदिम चर के लिए हैंडलिंग प्राप्त की जा रही है।

इसे अपने कोड के लिए आज़माएं।


2

बस एक साधारण डेकोरेटर

class overload:
    def __init__(self, f):
        self.cases = {}

    def args(self, *args):
        def store_function(f):
            self.cases[tuple(args)] = f
            return self
        return store_function

    def __call__(self, *args):
        function = self.cases[tuple(type(arg) for arg in args)]
        return function(*args)

आप इसे इस तरह से उपयोग कर सकते हैं

@overload
def f():
    pass

@f.args(int, int)
def f(x, y):
    print('two integers')

@f.args(float)
def f(x):
    print('one float')


f(5.5)
f(1, 2)

इसे अपने उपयोग के मामले में अनुकूलित करने के लिए इसे संशोधित करें।

अवधारणाओं का स्पष्टीकरण

  • समारोह प्रेषण : एक ही नाम के साथ कई कार्य हैं। किसको बुलाया जाए? दो रणनीतियों
  • स्थिर / संकलन-समय प्रेषण ( उर्फ "ओवरलोडिंग" )। तय करें कि कौन से फ़ंक्शन को संकलन-समय प्रकार के तर्कों के आधार पर कॉल करना है। सभी गतिशील भाषाओं में, कोई संकलन-समय प्रकार नहीं है, इसलिए परिभाषा के अनुसार ओवरलोडिंग असंभव है
  • डायनामिक / रन-टाइम प्रेषण : निर्णय लें कि रनटाइम प्रकार के तर्कों के आधार पर किस फ़ंक्शन को कॉल करना है। यह सभी ओओपी भाषाएं करते हैं: कई वर्गों में एक ही तरीके होते हैं, और भाषा तय करती है कि किसको self/thisतर्क के आधार पर कॉल करना है। हालाँकि, अधिकांश भाषाएँ केवल thisतर्क के लिए ही करती हैं। उपरोक्त डेकोरेटर विचार को कई मापदंडों तक बढ़ाता है।

स्पष्ट करने के लिए, एक स्थिर भाषा मान लें, और कार्यों को परिभाषित करें

void f(Integer x):
    print('integer called')

void f(Float x):
    print('float called')

void f(Number x):
    print('number called')


Number x = new Integer('5')
f(x)
x = new Number('3.14')
f(x)

स्टैटिक डिस्पैच (ओवरलोडिंग) के साथ आप दो बार "नंबर" देखेंगे, क्योंकि xइसे घोषित किया गया है Number, और यह सब ओवरलोडिंग के बारे में है। डायनेमिक डिस्पैच के साथ आपको "पूर्णांक कहा जाता है, फ्लोट कहा जाता है" दिखाई देगा, क्योंकि वे xउस समय के वास्तविक प्रकार हैं जो फ़ंक्शन कहलाते हैं।


यह उदाहरण महत्वपूर्ण रूप से यह नहीं बताता है किx डायनेमिक प्रेषण के लिए किस विधि को बुलाया गया है , और न ही किस क्रम में दोनों विधियों को स्थिर प्रेषण के लिए बुलाया गया है। अनुशंसा करें कि आप प्रिंट स्टेटमेंट print('number called for Integer')आदि को संपादित करें
स्माइली करें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.