第二十八篇 静态属性、类方法、静态方法

静态属性

静态属性:实际上说的就是数据属性

 

需求:每个人都有自己的房子,想知道每个人自己的房子都有多大平米

实现分析:
每个人,表示有很多人,不可能一个人写一次计算面积的方法,那么就可以将它提炼出来,放到类里
class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh
    
    # 计算面积
    def cal_area(self):
        # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length))

# 实例化
R1=Room('大House','alex',100,100,100000)
R2=Room('小平房','yuanhao',2,4,6)

# 调用计算面积
r1.cal_area()
r2.cal_area()

# alex 住的 大House 总面积是10000
# yuanhao 住的 小平房 总面积是8
View Code
  • 静态属性:@property

静态属性是绑定到实例上的,是通过实例.方法名 进行调用的。

静态属性的作用:就是封装逻辑,让用户再调用的时候完全感知不到后端的逻辑,就行在调用一个普通的数据属性一样。

特点:定义一个静态属性,内部会有一个self位置参数,self代表实例,实例可以访问到实例的数据属性,实例同样也可以访问到类的数据属性和类的函数属性,也就是说

静态属性,既可以访问实例属性,也可以访问类的属性(类的数据属性和类的函数属性)--->  self.类属性   或者  self.实例属性

class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property   # 属性的意思。
    def cal_area(self):
        return  '%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)   # 需要return值

# 实例化
R1=Room('大House','alex',100,100,100000)
R2=Room('小平房','yuanhao',2,4,6)

# 调用静待属性时,就不用再加小括号了。
# 调用cal_area,实际上是个函数属性,但是加了@property 之后,再调用,直观感受上就跟调用实例的数据属性是一样的,调用者看不到背后的运行逻辑,
# 调用者更不知道调用的这个到底是数据属性还是函数属性----这就是静态属性的作用。
print(R1.cal_area) print(R2.cal_area) # 这是实例直接调用实例的数据属性。 print(R1.name) print(R2.name)
  • 类方法:@calssmethod

不跟任何实例捆绑,只跟类捆绑的方法

作用和目的:跟实例没有任何关系了,只是类级别的操作,类来调用自己的方法。

特点:函数的位置参数默认就被写成了cls, cls代表类, 类能访问到类的数据属性和类的函数属性(---> cls.类属性 或者 cls.实例属性),但是类不能访问到实例的属性。

需求:
类调用类的函数属性
class Room:
    tag=1  # 类的数据属性
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property
    def cal_area(self):
        return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length)

    def test(self):
        print('from test',self.name)

# 1. 类可以调用自己(类)的数据属性
print(Room.tag)   # 1

# 2.  类调用自己(类)的函数属性
#  如果没有实例,直接调用函数属性
Room.test(r1)     # 1.name
# AttributeError: 'int' object has no attribute 'name'

# 报错了,因为函数属性test(self), self指的就是实例,需要传入实例参数才能正常运行。所以要先实例化
r1=Room('大House','alex',100,100,100000)
Room.test(r1)     # r1.name
#from test 大House
没有类方法之前的这种方案,必须通过实例才能调用到

使用类方法,就可以不再通过实例,而可以直接调用类的函数属性了。

class Room:
    tag=1  # 类的数据属性
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property   # 静态属性
    def cal_area(self):
        return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length)

    def test(self):
        print('from test',self.name)

    @classmethod  # 类方法:就把tell_info变成专门供类使用的方法
    def tell_info(cls,x):   # cls:本质上就是一个位置参数,但是python告诉你,cls接收的是一个类名,而这个类名是默认传的,不需要你再显示的写出来然后传进来
        # 打印类名
        print(cls)     # 运行结果:<class '__main__.Room'>  class表示类型是类,__main__表示当前文件,Room表示类名
        # cls.tag:类调用类的数据属性。
        print('--》',cls.tag)  # 相当于print('--》',Room.tag)


# 调用方式:类名.函数属性
# tell_info(cls,x): 有两个参数:第一个是位置参数cls,第二个是x
# 可以为什么只传了一个参数12呢?因为该函数属性已经变成了类方法,第一个参数cls Python默认就给你传成了类名Room
# 与self道理一样一样的。所以,调用者只需要传入第二个位置参数就可以拉。
Room.tell_info(12)
# 结果
<class '__main__.Room'>
--》 1

看到了把,用了类方法,类想调用类的函数属性,就不比再通过实例这个中介来调用了,而是可以直接调用。

注意:

# 通过实例也可以调用类方法

r1=Room('大House','alex',100,100,100000)
r1.tell_info(13)

# 结果
<class '__main__.Room'>
--》 1 13


# 但是,前面说过了,@classmethod 就是专门供类使用的,所以,虽然能通过实例调用,但是千万别这么干,别给自己找不痛快
注意,千万别给自己找别扭
  • 静态方法:@staticmethod

@staticmethod,不跟类绑定,也不跟实例绑定,只是一个类的工具包

用处:只是名义上归属类管理,不能使用类变量和实例变量,是类的工具包

特定:函数里既没有self参数,也没有cls参数,所以静态方法不能访问类属性和实例属性

class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property
    def cal_area(self):
        return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length)

    @classmethod
    def tell_info(cls,x):
        print(cls)
        print('--》',cls.tag,x)    #print('--》',Room.tag)

    @staticmethod     # static:静态  staticmethod 就是类的工具包
    def wash_body(a,b,c):
        print('%s、 %s、 %s正在洗澡' %(a,b,c))

  # 通俗的说,静态方法里没有self或者cls等位置参数,让你可以向类方法或者self那样,通过点的方式调用类变量,比如:通过cls.tag这样的方式进行调用类变量
  # 在类里,这么写是没有任何实际意义的, 所以,此处这么写就是为了提示自己,这是无意义的,以后开发中别这么干
def test(x,y): print(x,y) # @staticmethod 既可以通过类调用,也可以通过实例调用 # 1. 调用方式: 类名.函数属性名 Room.wash_body('alex','yuanhao','wupeiqi') # 2. 调用方式: 实例.函数属性名 r1=Room('厕所','alex',100,100,100000) r1.wash_body('alex','yuanhao','wupeiqi') # 那不加@staticmethod,有什么区别呢? # 1. 通过类调用test()函数,可以调用 Room.test(1,2) # 1 2 # 2. 通过实例调用test()函数,不可以调用,因为实例调用需要会自动把实例自己传给函数,而test()却没有self接收。 r1.test(1,2) # TypeError: test() takes 2 positional arguments but 3 were given

问题:那静态方法到底定义到哪里去了呢?

【答】静态属性@property、类方法@classmethod、静态方法@staticmethod, 都是定义到了类的数据字典里的。

print(Room.__dict__)

# {'__module__': '__main__', 'tag': 1, '__init__': <function Room.__init__ at 0x019A1C00>, 
'cal_area': <property object at 0x0193CDE0>, 'tell_info': <classmethod object at 0x017993B0>,
'wash_body': <staticmethod object at 0x019A5190>,
'test': <function Room.test at 0x019A1AE0>, '__dict__': <attribute '__dict__' of 'Room' objects>,
'__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
# 通过上面可以看到,静态属性@property、类方法@classmethod、静态方法@staticmethod, 都是定义到了类的数据字典里的。
print(r1.__dict__) # {'name': '厕所', 'owner': 'alex', 'width': 100, 'length': 100, 'heigh': 100000} #实例始终不变的就是只有自己的数据属性

 

小总结:

1、函数属性有参数self:表示该函数属性跟实例绑定

2、@classmethod,函数属性有参数cls:表示该函数属性跟类绑定

3、@staticmethod,函数属性既没有self参数,也没有cls参数:表示该函数属性既不跟类绑定,也不跟实例绑定,只是一个类的工具包

 

posted @ 2018-07-15 19:10  mamingchen  阅读(440)  评论(0编辑  收藏  举报