如何在Python中创建类(即静态)变量或方法?
当前回答
例如,如果您试图共享一个静态变量,在其他实例之间增加它,则类似以下脚本的操作很正常:
# -*- coding: utf-8 -*-
class Worker:
id = 1
def __init__(self):
self.name = ''
self.document = ''
self.id = Worker.id
Worker.id += 1
def __str__(self):
return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')
class Workers:
def __init__(self):
self.list = []
def add(self, name, doc):
worker = Worker()
worker.name = name
worker.document = doc
self.list.append(worker)
if __name__ == "__main__":
workers = Workers()
for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
workers.add(item[0], item[1])
for worker in workers.list:
print(worker)
print("next id: %i" % Worker.id)
其他回答
您可以创建类变量x、实例变量名、实例方法test1(self)、类方法test2(cls)和静态方法test3(),如下所示:
class Person:
x = "Hello" # Class variable
def __init__(self, name):
self.name = name # Instance variable
def test1(self): # Instance method
print("Test1")
@classmethod
def test2(cls): # Class method
print("Test2")
@staticmethod
def test3(): # Static method
print("Test3")
我在回答中解释了类变量,在回答中说明了类方法和静态方法,并在回答中介绍了实例方法。
@数据类定义提供用于定义实例变量和初始化方法__init__()的类级名称。如果要在@dataclass中使用类级变量,则应使用typeing.ClassVar类型提示。ClassVar类型的参数定义类级别变量的类型。
from typing import ClassVar
from dataclasses import dataclass
@dataclass
class Test:
i: ClassVar[int] = 10
x: int
y: int
def __repr__(self):
return f"Test({self.x=}, {self.y=}, {Test.i=})"
用法示例:
> test1 = Test(5, 6)
> test2 = Test(10, 11)
> test1
Test(self.x=5, self.y=6, Test.i=10)
> test2
Test(self.x=10, self.y=11, Test.i=10)
静态和类方法
正如其他答案所指出的,静态和类方法可以使用内置的修饰符轻松完成:
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
通常,my_method()的第一个参数绑定到类实例对象。相反,my_class_method()的第一个参数绑定到类对象本身(例如,在本例中为Test)。对于my_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=12向x添加了一个新的实例属性i,而不是更改测试类i属性的值。
部分预期的静态变量行为,即在多个实例之间同步属性(但不与类本身同步;请参见下面的“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)
现在您可以:
x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i # no error
assert x2.i == 50 # the property is synced
静态变量现在将在所有类实例之间保持同步。
(注意:除非类实例决定定义自己版本的_i!但如果有人决定这样做,他们应该得到什么,不是吗??)
注意,从技术上讲,i仍然不是一个“静态变量”;它是一种属性,是一种特殊类型的描述符。然而,属性行为现在相当于跨所有类实例同步的(可变)静态变量。
不可变的“静态变量”
对于不可变的静态变量行为,只需省略属性setter:
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)
现在尝试设置实例i属性将返回AttributeError:
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.i行产生错误,因为Test和x的i属性是两个不同的对象。
许多人会觉得这令人惊讶。然而,它不应该是。如果我们回去检查我们的测试类定义(第二个版本),我们会注意到这一行:
i = property(get_i)
显然,Test的成员i必须是属性对象,这是从属性函数返回的对象类型。
如果您发现上述问题令人困惑,那么您很可能仍然从其他语言(例如Java或c++)的角度来考虑它。您应该研究属性对象、Python属性的返回顺序、描述符协议和方法解析顺序(MRO)。
我提出了一个解决上述问题的方法;然而,我强烈建议,至少在你彻底理解为什么断言Test.I=x.I会导致错误之前,不要尝试执行以下操作。
实际静态变量-测试.i==x.i
我在下面介绍(Python3)解决方案,仅供参考。我并不赞同这是一个“好的解决方案”。我怀疑是否真的有必要在Python中模拟其他语言的静态变量行为。然而,不管它是否实际有用,下面的内容应该有助于进一步了解Python的工作原理。
更新:这种尝试真的很糟糕;如果你坚持这样做(提示:请不要这样做;Python是一种非常优雅的语言,不需要强迫它表现得像另一种语言),请使用Ethan Furman答案中的代码。
使用元类模拟其他语言的静态变量行为
元类是类的类。Python中所有类的默认元类(即,我认为Python 2.3之后的“新样式”类)是类型。例如:
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来工作。
“静态变量”的目录存储在StaticVarMeta.statics属性中。最初尝试使用替代解析顺序解析所有属性请求。我将其称为“静态解决顺序”或“SRO”。这是通过在给定类(或其父类)的“静态变量”集合中查找所请求的属性来完成的。如果该属性未出现在“SRO”中,则类将返回默认的属性get/set/delete行为(即“MRO”)。
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中是不可变的。这意味着由给定名称引用的对象如果属于上述对象类型之一,则不能更改。可以将名称重新分配给不同的对象,但不能更改对象本身。
通过禁止变量名指向除当前指向的对象之外的任何对象,使变量成为静态变量更进一步。(注意:这是一个通用的软件概念,并不特定于Python;请参阅其他人的帖子,了解有关在Python中实现静态的信息)。
例如,如果您试图共享一个静态变量,在其他实例之间增加它,则类似以下脚本的操作很正常:
# -*- coding: utf-8 -*-
class Worker:
id = 1
def __init__(self):
self.name = ''
self.document = ''
self.id = Worker.id
Worker.id += 1
def __str__(self):
return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')
class Workers:
def __init__(self):
self.list = []
def add(self, name, doc):
worker = Worker()
worker.name = name
worker.document = doc
self.list.append(worker)
if __name__ == "__main__":
workers = Workers()
for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
workers.add(item[0], item[1])
for worker in workers.list:
print(worker)
print("next id: %i" % Worker.id)
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录