首页 > Python > 如何在 Python 中创建类(即静态)变量或方法?

如何在 Python 中创建类(即静态)变量或方法?

上一篇 下一篇

如何在 Python 中创建类(即静态)变量或方法?

分割线

网友回答:

@Blair Conrad 说,在类定义中声明的静态变量,但不在方法内部声明的是类或“静态”变量:

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

这里有一些陷阱。从上面的例子继续:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

请注意,当属性直接设置为 时,实例变量如何与“静态”类变量不同步。这是因为在命名空间中重新绑定,命名空间不同于命名空间。如果要更改“静态”变量的值,则必须在最初定义该变量的范围(或对象)内更改该变量。我把“static”放在引号里,因为Python并没有像C++和Java那样真正有静态变量。t.iititTest

虽然它没有说明任何关于静态变量或方法的具体内容,但 Python 教程提供了一些关于类和类对象的相关信息。

@Steve Johnson 还回答了静态方法,也记录在 Python 库参考的“内置函数”下。

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beid还提到了类方法,它类似于静态方法。类方法的第一个参数是类对象。例:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

上述示例的图形表示

网友回答:

在类定义中声明但不在方法中声明的变量是类或静态变量:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

正如@millerdev指出的那样,这将创建一个类级变量,但这不同于任何实例级变量,因此您可以拥有ii

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

这与 C++ 和 Java 不同,但与 C# 没有太大区别,在 C# 中,无法使用对实例的引用访问静态成员。

看看 Python 教程对类和类对象主题有什么看法。

@Steve Johnson 已经回答了静态方法,也记录在 Python 库参考的“内置函数”下。

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@beidy 推荐类方法而不是静态方法,因为该方法随后接收类类型作为第一个参数。

分割线

网友回答:

静态方法和类方法

正如其他答案所指出的,使用内置装饰器可以轻松完成静态和类方法:

class Test(object):

    # regular instance method:
    def my_method(self):
        pass

    # class method:
    @classmethod
    def my_class_method(cls):
        pass

    # static method:
    @staticmethod
    def my_static_method():
        pass

像往常一样,第一个参数绑定到类实例对象。相反,第一个参数 to 绑定到类对象本身(例如,在这种情况下,)。对于 ,没有绑定任何参数,并且具有参数是可选的。my_method()my_class_method()Testmy_static_method()

“静态变量”

然而,实现“静态变量”(好吧,可变的静态变量,无论如何,如果这不是一个矛盾的话……)并不那么简单。正如 millerdev 在他的回答中指出的那样,问题在于 Python 的类属性并不是真正的“静态变量”。考虑:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

这是因为该行添加了一个新的实例属性,而不是更改类属性的值。x.i = 12ixTesti

部分预期的静态变量行为,即在多个实例之间同步属性(但不与类本身同步;请参阅下面的“gotcha”),可以通过将类属性转换为属性来实现:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

Now you can do:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

静态变量现在将在所有类实例之间保持同步。

(注意:也就是说,除非类实例决定定义自己的 !但是,如果有人决定这样做,他们应该得到他们得到的,不是吗???)_i

请注意,从技术上讲,它仍然不是一个“静态变量”;它是一个 ,这是一种特殊类型的描述符。但是,该行为现在等效于跨所有类实例同步的(可变)静态变量。ipropertyproperty

不可变的“静态变量”

对于不可变的静态变量行为,只需省略资源库:property

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

现在尝试设置实例属性将返回:iAttributeError

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

一个需要注意的陷阱

请注意,上述方法仅适用于类的实例它们在使用类本身时不起作用。所以例如:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

该行产生错误,因为 和 的属性是两个不同的对象。assert Test.i == x.iiTestx

许多人会觉得这很令人惊讶。但是,它不应该是。如果我们返回并检查我们的类定义(第二个版本),我们会注意到这一行:Test

    i = property(get_i) 

显然,的成员必须是一个对象,这是从函数返回的对象类型。iTestpropertyproperty

如果你发现上述令人困惑,你很可能仍在从其他语言(例如Java或c ++)的角度来思考它。你应该去研究这个对象,关于 Python 属性的返回顺序、描述符协议和方法解析顺序 (MRO)。property

我在下面提出了上述“陷阱”的解决方案;但是,我强烈建议您不要尝试执行以下操作,直到至少完全了解导致错误的原因。assert Test.i = x.i

真实,实际静态变量 –Test.i == x.i

我在下面介绍(Python 3)解决方案仅供参考。我不赞同它是一个“好的解决方案”。我怀疑在Python中模拟其他语言的静态变量行为是否真的必要。但是,无论它是否真的有用,以下内容都应该有助于进一步理解Python的工作原理。

更新:这种尝试真的非常糟糕;如果你坚持做这样的事情(提示:请不要;Python是一种非常优雅的语言,没有必要将其表现得像另一种语言),而是使用Ethan Furman答案中的代码。

使用元类模拟其他语言的静态变量行为

元类是类的类。Python 中所有类的默认元类(即我相信 Python 2.3 之后的“新样式”类)是 。例如:type

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

但是,您可以像这样定义自己的元类:

class MyMeta(type): pass

并像这样将其应用于您自己的类(仅限 Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

下面是我创建的元类,它试图模拟其他语言的“静态变量”行为。它基本上通过将默认的getter,setter和deleter替换为检查所请求的属性是否为“静态变量”的版本来工作。

“静态变量”的目录存储在属性中。最初尝试使用替换解析顺序解析所有属性请求。我称之为“静态解析顺序”或“SRO”。这是通过在给定类(或其父类)的“静态变量”集中查找请求的属性来完成的。如果该属性未出现在“SRO”中,则类将回退到默认属性获取/设置/删除行为(即“MRO”)。StaticVarMeta.statics

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!
    
    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 
    
    Example usage: 
        
        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)
            
        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)
            
        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))
                        
        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

模板简介:该模板名称为【如何在 Python 中创建类(即静态)变量或方法?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Python】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 326次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-15
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信模板 微信图片 html5 响应式 企业网站 微信文章 单页式简历模板 自适应 微信公众平台 微信素材
您可能会喜欢的其他模板