Soru Python sınıfındaki sabitleri tanımlamak gerçekten kendine ihtiyaç duyuyor mu?


Bir sınıftaki gibi bir sabitler kümesini tanımlamak istiyorum:

class Foo(object):
   (NONEXISTING,VAGUE,CONFIRMED) = (0,1,2)
   def __init__(self):
       self.status = VAGUE

Ancak ben

NameError: global name 'VAGUE' is not defined

Bu sabitleri, sınıf içinde ziyaret edilmeksizin tanımlanabilmenin bir yolu var mı? global veya self.NONEXISTING = 0 vb.?


25
2017-10-22 09:33


Menşei




Cevaplar:


Sınıf gövdesindeki isimlere atadığınızda, sınıfın niteliklerini yaratırsınız. Bunlara, doğrudan ya da dolaylı olarak sınıfa başvurmadan başvuramazsınız. Kullanabilirsiniz Foo.VAGUE diğer cevapların dediği gibi, ya da self.VAGUE. zorunda değilsin atamak özelliklerine self.

Genellikle, kullanarak self.VAGUE İstediğiniz şeydir çünkü alt sınıfların, özniteliği, onları kullanan tüm yöntemleri yeniden kullanma gereği duymadan yeniden tanımlamasına izin verir - bu özel örnekte mantıklı bir şey gibi görünmez, ama kim bilir.


36
2017-10-22 10:09





yerine deneyin:

self.status = VAGUE

Bu:

self.status = Foo.VAGUE

sınıfı belirtmelisin


7
2017-10-22 09:37





Tek yol, sınıf adıyla erişmektir.

Foo.VAGUE

İçinde sadece VAGUE erişiyorsanız __init__ işlev veya bir işlev, istediğiniz şekilde erişmek için bunun içinde bildirilmelidir.

Kendini kullanmak da sınıfın örneğidir.


3
2017-10-22 09:39



Tamam, bu çok kötü. Java'da yapmaya çalıştığım gibi bir şey yapabileceğini hatırlıyorum. Emin değilim. - Theodor
Java'da sorduğun şeyi yapabileceğine eminim. - Iacks
Ve eminim ki, java'da yapmak istediğiniz diğer şeylerin% 90'ını yapamazsınız. - aaronasterling


Bu herhangi bir yolla HERHANGİ BİR KOD İÇİN TAVSİYE EDİLMEMİŞTİR, ancak aşağıdaki gibi çirkin bir hack yapılabilir. Bunu Python AST API'sini daha iyi anlamak için yaptım, bunu gerçek dünyadaki kodda kullanan herkes zarar vermeden önce vurulmalıdır :-)

#!/usr/bin/python
# -*- coding: utf-8-unix -*-
#
# AST hack to replace symbol reference in instance methods,
# so it will be resolved as a reference to class variables.
#

import inspect, types, ast

def trim(src):
    lines = src.split("\n")
    start = lines[0].lstrip()
    n = lines[0].index(start)
    src = "\n".join([line[n:] for line in lines])
    return src

#
# Method decorator that replaces symbol reference in a method
# so it will use symbols in belonging class instead of the one
# in global namespace.
#
def nsinclude(*args):
    # usecase: @nsinclude()
    # use classname in calling frame as a fallback
    stack = inspect.stack()
    opts  = [stack[1][3]]

    def wrap(func):
        if func.func_name == "tempfunc":
            return func

        def invoke(*args, **kw):
            base = eval(opts[0])

            src = trim(inspect.getsource(func))
            basenode = ast.parse(src)

            class hackfunc(ast.NodeTransformer):
                def visit_Name(self, node):
                    try:
                        # if base class (set in @nsinclude) can resolve
                        # given name, modify AST node to use that instead
                        val = getattr(base, node.id)

                        newnode = ast.parse("%s.%s" % (opts[0], node.id))
                        newnode = next(ast.iter_child_nodes(newnode))
                        newnode = next(ast.iter_child_nodes(newnode))
                        ast.copy_location(newnode, node)
                        return ast.fix_missing_locations(newnode)
                    except:
                        return node

            class hackcode(ast.NodeVisitor):
                def visit_FunctionDef(self, node):
                    if func.func_name != "tempfunc":
                        node.name = "tempfunc"
                        hackfunc().visit(node)

            hackcode().visit(basenode)

            newmod = compile(basenode, '<ast>', 'exec')
            eval(newmod)
            newfunc = eval("tempfunc")
            newfunc(*args, **kw)
        return invoke


    # usecase: @nsinclude
    if args and isinstance(args[0], types.FunctionType):
        return wrap(args[0])

    # usecase: @nsinclude("someclass")
    if args and args[0]:
        opts[0] = args[0]
    return wrap

class Bar:
    FOO = 987
    BAR = 876

class Foo:
    FOO = 123
    BAR = 234

    # import from belonging class
    @nsinclude
    def dump1(self, *args):
        print("dump1: FOO = " + str(FOO))


    # import from specified class (Bar)
    @nsinclude("Bar")
    def dump2(self, *args):
        print("dump2: BAR = " + str(BAR))

Foo().dump1()
Foo().dump2()

3
2017-07-12 13:47



Çaba ve yansıma hack için +1. Bu senaryoda çok yararlı değil, ancak yansıma kesmek her zaman eğlencelidir! - vlad-ardelean


Python3'te ayrıca başvuru yapabilirsiniz VAGUE gibi:

type(self).VAGUE

Bu şekilde, onu açıkça bir sınıf özniteliği olarak değil, bir öznitelik özniteliği olarak görüyorsunuz, ancak bu yol, sınıfın ad değişikliğine karşı sağlamdır. Ayrıca geçersiz kılsanız VAGUE Bir alt sınıfta, eğer kullanmış olsaydınız, alt sınıftaki değer kullanılır. self.VAGUE.

Bu yöntemin en azından benim testlerimde değil, Python2'de çalıştığı görülüyor. type(self) iade instance Anladığım sınıf yerine. Bu nedenle, Thomas Wouters'ın yanıtı, yaygın Python2'nin ne kadar yaygın olduğu göz önüne alındığında muhtemelen tercih edilir.


3
2018-03-27 10:05