سؤال

أريد أن "يعمل فقط":

def main():
    c = Castable()
    print c/3
    print 2-c
    print c%7
    print c**2
    print "%s" % c
    print "%i" % c
    print "%f" % c

بالطبع ، الطريق السهل هو الكتابة int(c)/3, ، لكني أود تمكين بناء جملة بيرل-أيش أبسط من أجل اللغة المصغرة للتكوين.

من الجدير بالذكر أنه إذا استخدمت فئة "على الطراز القديم" (لا ترث من كائن) يمكنني القيام بذلك ببساطة عن طريق تحديد أ __coerce__ الطريقة ، ولكن يتم إهمال الفصول الدراسية على الطراز القديم وسيتم إزالتها في Python3.

عندما أفعل نفس الشيء مع فصل دراسي جديد ، أحصل على هذا الخطأ:

TypeError: unsupported operand type(s) for /: 'Castable' and 'int'

أعتقد أن هذا حسب التصميم ، ولكن كيف يمكنني محاكاة النمط القديم __coerce__ السلوك مع فصل على الطراز الجديد؟ يمكنك العثور على الحل الحالي أدناه ، لكنه قبيح للغاية وطويل.

هذه هي الوثائق ذات الصلة: (أعتقد)

نقاط المكافأة:

    print pow(c, 2, 100)
هل كانت مفيدة؟

المحلول 2

يعمل هذا ، وهو أقل إجماعًا بعد العديد من التحسينات (الدعائم إلى jchl) ، ولكن لا يزال يبدو أنه يجب أن يكون غير ضروري ، خاصة بالنظر إلى أنك تحصل على هذا مجانًا مع فصول "الطراز القديم".

ما زلت أبحث عن إجابة أفضل. إذا لم تكن هناك طريقة أفضل ، يبدو لي هذا مثل الانحدار في لغة بيثون.

def ops_list():
    "calculate the list of overloadable operators"
    #<type 'object'> has functions but no operations
    not_ops = dir(object)

    #calculate the list of operation names
    ops = set()
    for mytype in (int, float, str):
        for op in dir(mytype):
            if op.endswith("__") and op not in not_ops:
                ops.add(op)
    return sorted(ops)

class MetaCastable(type):
    __ops = ops_list()

    def __new__(mcs, name, bases, dict):
        #pass any undefined ops to self.__op__
        def add_op(op):
            if op in dict:
                return
            fn = lambda self, *args: self.__op__(op, args)
            fn.__name__ = op
            dict[op] = fn

        for op in mcs.__ops:
            add_op( op )
        return type.__new__(mcs, name, bases, dict)


class Castable(object):
    __metaclass__ = MetaCastable
    def __str__(self):
        print "str!"
        return "<Castable>"
    def __int__(self):
        print "int!"
        return 42
    def __float__(self):
        print "float!"
        return 2.718281828459045

    def __op__(self, op, args):
        try:
            other = args[0]
        except IndexError:
            other = None
        print "%s %s %s" % (self, op, other)
        self, other = coerce(self, other)
        return getattr(self, op)(*args)

    def __coerce__(self, other):
        print "coercing like %r!" % other
        if other is None: other = 0.0
        return (type(other)(self), other)

نصائح أخرى

تحتاج إلى تحديد __div__ إن أردت c/3 للعمل. لن يقوم Python بتحويل كائنك إلى رقم أولاً لك.

class MetaCastable(type):
    __binary_ops = ( 
            'add', 'sub', 'mul', 'floordiv', 'mod', 'divmod', 'pow', 'lshift', 
            'rshift', 'and', 'xor', 'or', 'div', 'truediv',
    )

    __unary_ops = ( 'neg', 'pos', 'abs', 'invert', )

    def __new__(mcs, name, bases, dict):
        def make_binary_op(op):
            fn = lambda self, other: self.__op__(op, other)
            fn.__name__ = op
            return fn

        for opname in mcs.__binary_ops:
            for op in ( '__%s__', '__r%s__' ):
                op %= opname
                if op in dict:
                    continue
                dict[op] = make_binary_op(op)

        def make_unary_op(op):
            fn = lambda self: self.__op__(op, None)
            fn.__name__ = op
            return fn

        for opname in mcs.__unary_ops:
            op = '__%s__' % opname
            if op in dict:
                continue
            dict[op] = make_unary_op(op)

        return type.__new__(mcs, name, bases, dict)

class Castable(object):
    __metaclass__ = MetaCastable
    def __str__(self):
        print "str!"
        return "<Castable>"
    def __int__(self):
        print "int!"
        return 42
    def __float__(self):
        print "float!"
        return 2.718281828459045

    def __op__(self, op, other):
        if other is None:
            print "%s(%s)" % (op, self)
            self, other = coerce(self, 0.0)
            return getattr(self, op)()
        else:
            print "%s %s %s" % (self, op, other)
            self, other = coerce(self, other)
            return getattr(self, op)(other)

    def __coerce__(self, other):
        print "coercing like %r!" % other
        return (type(other)(self), other)
class Castable(object):
    def __div__(self, other):
        return 42 / other

تعمل فصول الأناقة الجديدة بشكل أسرع وأكثر دقة من فصول النمط القديمة. وبالتالي ليس أكثر تكلفة __getattr__, __getattribute__ , __coerce__ يدعو إلى أي أسباب رخيصة وبترتيب مشكوك فيه.

النمط القديم __coerce__ واجهت أيضًا المشكلة ، والتي تم استدعاؤها حتى عندما تكون قد أدت بالفعل إلى زيادة تحميل طريقة المشغل لغرض خاص. ويطالب الصب بالأنواع المشتركة المتساوية ، ويقتصر على بعض العمليات الثنائية. فكر في جميع الأساليب والخصائص الأخرى لـ int / float / string - وحول POW (). بسبب كل هذه القيود coerce مفقود في PY3. تهدف أمثلة السؤال إلى المحاكاة الافتراضية الواسعة إلى حد ما.

من خلال فئات النمط الجديدة ، فإن حلقة ما هي مجرد حلقة لتوفير العديد من الأساليب "المماثلة" مع القليل من التعليمات البرمجية ، أو توجيه تلك المكالمات إلى معالج افتراضي ثم سريعًا ومحددًا بدقة وتصنيفه بطريقة صحيح وغرامة. هذا ليس "الانحدار في لغة بيثون".

ومع ذلك ، لن أستخدم فصلًا ميتا كما هو موضح في إجابات أخرى لمثل هذه الحلقة أو لتوفير نوع من السلوك الأساسي البسيط. من شأنه أن يكسر الجوز مع مطرقة ثقيلة.


هنا مثال مساعد للمحاكاة الافتراضية لـ "البديل":

def Virtual(*methods):
    """Build a (new style) base or mixin class, which routes method or
    operator calls to one __virtualmeth__ and attribute lookups to
    __virtualget__ and __virtualset__ optionally.

    *methods (strings, classes): Providing method names to be routed
    """
    class VirtualBase(object):  
        def __virtualmeth__(self, methname, *args, **kw):
            raise NotImplementedError
    def _mkmeth(methname, thing):
        if not callable(thing):
            prop = property(lambda self:self.__virtualget__(methname),
                            lambda self, v:self.__virtualset__(methname, v))
            return prop
        def _meth(self, *args, **kw):
            return self.__virtualmeth__(methname, *args, **kw)
        _meth.__name__ = methname
        return _meth
    for m in methods:
        for name, thing in (isinstance(m, str) and
                            {m:lambda:None} or m.__dict__).items():
            if name not in ('__new__', '__init__', '__setattr__', ##'__cmp__',
                            '__getattribute__', '__doc__', ):   ##'__getattr__', 
                setattr(VirtualBase, name, _mkmeth(name, thing))
    return VirtualBase

وهنا مثال استخدام حالة: Anaphor! (PY2 و PY3):

import operator
class Anaphor(Virtual(int, float, str)):   
    """remember a sub-expression comfortably:

    A = Anaphor()      # at least per thread / TLS
    if re.search(...) >> A:
        print(A.groups(), +A)
    if A(x % 7) != 0:
        print(A, 1 + A, A < 3.0, A.real, '%.2f' % A, +A)
    """
    value = 0
    def __virtualmeth__(self, methname, *args, **kw):
        try: r = getattr(self.value, methname)(*args, **kw)
        except AttributeError:
            return getattr(operator, methname)(self.value, *args, **kw)
        if r is NotImplemented: # simple type -> coerce
            try: tcommon = type(self.value + args[0])    # PY2 coerce
            except: return NotImplemented
            return getattr(tcommon(self.value), methname)(*args, **kw)
        return r
    def __call__(self, value):   
        self.value = value
        return value
    __lshift__ = __rrshift__ = __call__     # A << x;  x >> A
    def __pos__(self):                      # real = +A
        return self.value
    def __getattr__(self, name):
        return getattr(self.value, name)
    def __repr__(self):
        return '<Anaphor:%r>' % self.value

بسلاسة يتولى أيضا Opertor 3-arg pow() :-) :

>>> A = Anaphor()
>>> x = 1
>>> if x + 11 >> A:
...     print repr(A), A, +A, 'y' * A, 3.0 < A, pow(A, 2, 100)
...     
<Anaphor:12> 12 12 yyyyyyyyyyyy True 44
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top