如何在Python中创建类(即静态)变量或方法?
当前回答
您可以创建类变量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")
我在回答中解释了类变量,在回答中说明了类方法和静态方法,并在回答中介绍了实例方法。
其他回答
总结其他人的回答并补充,在python中声明静态方法或变量有很多种方法。
1.使用staticmethod()作为装饰符:
可以简单地在声明的方法(函数)上方放置一个修饰符,使其成为静态方法。例如。
class Calculator:
@staticmethod
def multiply(n1, n2, *args):
Res = 1
for num in args: Res *= num
return n1 * n2 * Res
print(Calculator.multiply(1, 2, 3, 4)) # 24
2.使用staticmethod()作为参数函数:
此方法可以接收函数类型的参数,并返回传递函数的静态版本。例如。
class Calculator:
def add(n1, n2, *args):
return n1 + n2 + sum(args)
Calculator.add = staticmethod(Calculator.add)
print(Calculator.add(1, 2, 3, 4)) # 10
3.使用classmethod()作为装饰符:
@classmethod对函数的影响与@staticmethod类似,但是这一次,需要在函数中接受一个额外的参数(类似于实例变量的self参数)。例如。
class Calculator:
num = 0
def __init__(self, digits) -> None:
Calculator.num = int(''.join(digits))
@classmethod
def get_digits(cls, num):
digits = list(str(num))
calc = cls(digits)
return calc.num
print(Calculator.get_digits(314159)) # 314159
4.使用classmethod()作为参数函数:
@classmethod也可以用作参数函数,以防不想修改类定义。例如。
class Calculator:
def divide(cls, n1, n2, *args):
Res = 1
for num in args: Res *= num
return n1 / n2 / Res
Calculator.divide = classmethod(Calculator.divide)
print(Calculator.divide(15, 3, 5)) # 1.0
5.直接申报
在所有其他方法外部但在类内部声明的方法/变量自动是静态的。
class Calculator:
def subtract(n1, n2, *args):
return n1 - n2 - sum(args)
print(Calculator.subtract(10, 2, 3, 4)) # 1
整个计划
class Calculator:
num = 0
def __init__(self, digits) -> None:
Calculator.num = int(''.join(digits))
@staticmethod
def multiply(n1, n2, *args):
Res = 1
for num in args: Res *= num
return n1 * n2 * Res
def add(n1, n2, *args):
return n1 + n2 + sum(args)
@classmethod
def get_digits(cls, num):
digits = list(str(num))
calc = cls(digits)
return calc.num
def divide(cls, n1, n2, *args):
Res = 1
for num in args: Res *= num
return n1 / n2 / Res
def subtract(n1, n2, *args):
return n1 - n2 - sum(args)
Calculator.add = staticmethod(Calculator.add)
Calculator.divide = classmethod(Calculator.divide)
print(Calculator.multiply(1, 2, 3, 4)) # 24
print(Calculator.add(1, 2, 3, 4)) # 10
print(Calculator.get_digits(314159)) # 314159
print(Calculator.divide(15, 3, 5)) # 1.0
print(Calculator.subtract(10, 2, 3, 4)) # 1
有关掌握Python中的OOP,请参阅Python文档。
用这种方式,静态变量是在用户定义的类出现时创建的,
class Student:
the correct way of static declaration
i = 10
incorrect
self.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
关于这个答案,对于常量静态变量,可以使用描述符。下面是一个示例:
class ConstantAttribute(object):
'''You can initialize my value but not change it.'''
def __init__(self, value):
self.value = value
def __get__(self, obj, type=None):
return self.value
def __set__(self, obj, val):
pass
class Demo(object):
x = ConstantAttribute(10)
class SubDemo(Demo):
x = 10
demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x
导致。。。
small demo 10
small subdemo 100
big demo 10
big subdemo 10
如果您不喜欢忽略设置值(上面的传递),您总是可以引发异常。如果您正在寻找C++、Java风格的静态类变量:
class StaticAttribute(object):
def __init__(self, value):
self.value = value
def __get__(self, obj, type=None):
return self.value
def __set__(self, obj, val):
self.value = val
请查看此答案和HOWTO官方文件,以了解有关描述符的更多信息。
就我个人而言,每当我需要静态方法时,我都会使用类方法。主要是因为我把课堂当作一个论点。
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
或使用装饰器
class myObj(object):
@classmethod
def myMethod(cls)
对于静态财产。。是时候查一下python的定义了。。变量始终可以更改。有两种类型是可变的和不可变的。。此外,还有类属性和实例属性。。没有什么东西真正像java&c意义上的静态属性++
为什么要使用Python意义上的静态方法,如果它与类没有任何关系!如果我是你,我要么使用classmethod,要么定义独立于类的方法。
推荐文章
- Numpy Max vs amax vs maximum
- 我应该在.gitignore文件中添加Django迁移文件吗?
- 每n行有熊猫
- 实例属性attribute_name定义在__init__之外
- 如何获取在Python中捕获的异常的名称?
- 第一次出现的值大于现有值的Numpy
- 如何从Python函数中返回两个值?
- 前一个月的Python日期
- Python中方括号括起来的列表和圆括号括起来的列表有什么区别?
- Python日志记录不输出任何东西
- 接口方法的最终参数-有什么意义?
- 每n秒运行特定代码
- SQLAlchemy是否有与Django的get_or_create等价的函数?
- 如何将python datetime转换为字符串,具有可读格式的日期?
- 美丽的汤和提取div及其内容的ID