python:类属性和实例属性、静态方法

Class and Instance Attributes

类属性

  • 一个类的实例拥有各自的实例属性(Instance attributes),所以不同的实例通常所带的实例属性是不一样的
  • 也可以在类级别上定义属性:类属性被这个类拥有,该类所有的实例都共享这个属性,因此对所有实例而言这个值都是一样的。
  • 通常定义类属性在所有方法之外,通常放在最上方类头(class header)的右下。
#例子
class A:
    a = "I am a class attribute!"
x = A()
y = A()
x.a
'I am a class attribute!'
y.a
'I am a class attribute!'
A.a
'I am a class attribute!'
  • 看出来可以通过类名或者实例访问类属性

  • 如果要改变类属性,必须通过类名.属性名(ClassName.AttributeName)进行更改。否则会创造一个新的实例变量。

class A:
    a = "I am a class attribute!"

x = A()
y = A()
x.a = "This creates a new instance attribute for x!"
y.a
'I am a class attribute!'
A.a
'I am a class attribute!'
A.a = "This is changing the class attribute 'a'!"
A.a
"This is changing the class attribute 'a'!"
y.a
"This is changing the class attribute 'a'!"
# but x.a is still the previously created instance variable:
x.a
'This creates a new instance attribute for x!'
  • python的类(class)属性和对象(object)属性存储在不同的字典中:
x.__dict__
{'a': 'This creates a new instance attribute for x!'}
y.__dict__
{}
A.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'a': "This is changing the class attribute 'a'!"})
x.__class__.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'a': "This is changing the class attribute 'a'!"})
  • 类属性的例子:一个机器人类。
    • 创建类属性Three_Laws,这是一个tuple包括了三个定律,这三个定律对每一个机器人实例都时要遵守的。
class Robot:
    Three_Laws = (
"""A robot may not injure a human being or, through inaction, allow a human being to come to harm.""",
"""A robot must obey the orders given to it by human beings, except where such orders would conflict with the First Law.,""",
"""A robot must protect its own existence as long as such protection does not conflict with the First or Second Law."""
)
    def __init__(self, name, build_year):
        self.name = name
        self.build_year = build_year

    # other methods as usual
for number, text in enumerate(Robot.Three_Laws):
    print(str(number+1) + ": \n" + text)
1: 
A robot may not injure a human being or, through inaction, allow a human being to come to harm.
2: 
A robot must obey the orders given to it by human beings, except where such orders would conflict with the First Law.,
3: 
A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.
  • 下面的例子:用类属性对实例数目计数
class C:
    counter = 0
    def __init__(self):
        type(self).counter += 1
    def __del__(self):
        #用type(self)而不是C.counter是有好处的,特别在这一个类作为超类(superclass)的时候
        type(self).counter -= 1

if __name__ == "__main__":
    x = C()
    print("Number of instances: : " + str(C.counter))
    y = C()
    print("Number of instances: : " + str(C.counter))
    del x
    print("Number of instances: : " + str(C.counter))
    del y
    print("Number of instances: : " + str(C.counter))
Number of instances: : 1
Number of instances: : 2
Number of instances: : 1
Number of instances: : 0

Static Methods

  • 之前把类属性作为共有属性(public attributes),现在要把之前的类属性作为私有属性使用,可以在前面添加双下划线。
  • 这样做之后定义实例方法(instance methods)获取和更改这些私有类属性。
class Robot:
    __counter = 0 
    def __init__(self):
        type(self).__counter += 1

    def RobotInstances(self):
        return Robot.__counter

if __name__ == "__main__":
    x = Robot()
    print(x.RobotInstances())
    y = Robot()
    print(x.RobotInstances())
1
2
  • 这不是一个好主意,原因有两个:
  • 第一,因为机器人的数量与单个机器人实例无关;
  • 第二,在没有创建实例之前我们不能查询机器人的数量。
  • 如果我们试图调用名为Robot.RobotInstance()的方法,就会得到一条错误消息,因为它需要一个实例作为参数。
Robot.RobotInstances()
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-26-2aa3399c6a33> in <module>()
----> 1 Robot.RobotInstances()


TypeError: RobotInstances() missing 1 required positional argument: 'self'
# 下一个想法仍然不能解决我们的问题,就是省略参数“Self”:
class Robot:
    __counter = 0

    def __init__(self):
        type(self).__counter += 1

    def RobotInstances():
        return Robot.__counter
Robot.RobotInstances()
0
# 现在可以通过类名访问方法,但不能通过实例调用它:
x = Robot()
x.RobotInstances()
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-30-1d5d0a91b7f1> in <module>()
      1 # 现在可以通过类名访问方法,但不能通过实例调用它:
      2 x = Robot()
----> 3 x.RobotInstances()


TypeError: RobotInstances() takes 0 positional arguments but 1 was given
  • 调用“x.RobotInstance()”被视为实例方法调用,实例方法需要将实例引用作为第一个参数。
  • 我们需要一个方法,我们可以通过类名或实例名调用它,而不需要传递对实例的引用。
  • 解决方案由静态方法(static methods)组成,这些方法不需要对实例的引用。
  • 将一个方法转换为静态方法只需要加上”@staticmethod”在方法名的上方,这是修饰器的语法。
class Robot:
    __counter = 0

    def __init__(self):
        type(self).__counter += 1

    @staticmethod
    def RobotInstances():
        return Robot.__counter

if __name__ == "__main__":
    print(Robot.RobotInstances())
    x = Robot()
    print(x.RobotInstances())
    y = Robot()
    print(x.RobotInstances())
    print(Robot.RobotInstances())
0
1
2
2
  • 类方法的用例:这些方法在所谓工厂方法的定义中使用,我们将在这里不讨论这些方法。
  • 它们经常被使用,当我们有静态方法时,必须调用其他静态方法。要做到这一点,我们必须硬编码类名,如果我们必须使用静态方法。这是一个问题,如果我们在用例中,我们继承了类。
#下面的程序包含一个分数类,q求两个或多个整数的最大公因子(GCD)
#我们可以通过将分子和分母除以最大公共除数(GCD),将一个分数降到最低。
#我们定义了一个静态GCD函数来计算两个数的最大公因子。
#两个或多个整数的最大公因子(GCD)(至少其中一个不是零)是除以没有余数的数的最大正整数。
#例如,8和24的GCD是8。
#静态方法“gcd”是由我们的类方法“还原”和“cls.gcd n1(N2)”调用的。“CLS”指的是“分数”。
class fraction(object):

    def __init__(self, n, d):
        self.numerator, self.denominator = fraction.reduce(n, d)


    @staticmethod
    def gcd(a,b):
        while b != 0:
            a, b = b, a%b
        return a

    @classmethod
    def reduce(cls, n1, n2):
        g = cls.gcd(n1, n2)
        return (n1 // g, n2 // g)

    def __str__(self):
        return str(self.numerator)+'/'+str(self.denominator)
x = fraction(8,24)
print(x)
1/3
#我们用“about”的方法定义了一个类“Pets”。
#这个类将在子类“Dogs”和“Cats”中继承。方法“about”也将被继承。
#我们将在第一个实现中将“about”方法定义为“静态方法”,以说明这种方法的缺点:
class Pets:
    name = "pet animals"

    @staticmethod
    def about():
        print("This class is about {}!".format(Pets.name))   


class Dogs(Pets):
    name = "'man's best friends' (Frederick II)"

class Cats(Pets):
    name = "cats"

p = Pets()
p.about()
d = Dogs()
d.about()
c = Cats()
c.about()
This class is about pet animals!
This class is about pet animals!
This class is about pet animals!
class Pets:
    name = "pet animals"

    @classmethod
    def about(cls):
        print("This class is about {}!".format(cls.name))

class Dogs(Pets):
    name = "'man's best friends' (Frederick II)"

class Cats(Pets):
    name = "cats"

p = Pets()
p.about()

d = Dogs()
d.about()

c = Cats()
c.about()
This class is about pet animals!
This class is about 'man's best friends' (Frederick II)!
This class is about cats!
  • 缺点是:“about”方法不知道它已被dog或cat类的实例调用。
# 我们现在用类方法装饰器来装饰它,而不是静态方法装饰器:
class Pets:
    name = "pet animals"

    @classmethod
    def about(cls):
        print("This class is about {}!".format(cls.name))

class Dogs(Pets):
    name = "'man's best friends' (Frederick II)"

class Cats(Pets):
    name = "cats"

p = Pets()
p.about()

d = Dogs()
d.about()

c = Cats()
c.about()
This class is about pet animals!
This class is about 'man's best friends' (Frederick II)!
This class is about cats!

参考:https://www.python-course.eu/python3_class_and_instance_attributes.php

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值