Python之特性、静态方法、类方法

特性property

1:什么是特性property

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。

 1 import math
 2 class Circle:
 3     def __init__(self,radius): #圆的半径radius
 4         self.radius=radius
 5 
 6     @property
 7     def area(self):
 8         return math.pi * self.radius**2 #计算面积
 9 
10     @property
11     def perimeter(self):
12         return 2*math.pi*self.radius #计算周长
13 
14 c=Circle(10)
15 print(c.radius)
16 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
17 print(c.perimeter) #同上
18 '''
19 输出结果:
20 314.1592653589793
21 62.83185307179586
22 '''

注意:此时的特性arear和perimeter不能被赋值

 

2: 为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则。

python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现。

 1 class Foo:
 2     def __init__(self,val):
 3         self.__NAME=val #将所有的数据属性都隐藏起来
 4 
 5     @property
 6     def name(self):
 7         return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
 8 
 9     @name.setter
10     def name(self,value):
11         if not isinstance(value,str):  #在设定值之前进行类型检查
12             raise TypeError('%s must be str' %value)
13         self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
14 
15     @name.deleter
16     def name(self):
17         raise TypeError('Can not delete')
18 
19 f=Foo('egon')
20 print(f.name)
21 # f.name=10 #抛出异常'TypeError: 10 must be str'
22 del f.name #抛出异常'TypeError: Can not delete'

  通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错。

 

@staticmethod means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).

 

@classmethod means: when this method is called, we pass the class as the first argument instead of the instance of that class (as we normally do with methods). This means you can use the class and its properties inside that method rather than a particular instance.

 

 

静态方法(staticmethod)

是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法。
class Foo:
    def spam(x,y,z): #类中的一个函数,千万不要懵逼,self和x啥的没有不同都是参数名
        print(x,y,z)
    spam=staticmethod(spam) #把spam函数做成静态方法
基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是

class Foo:
    @staticmethod #装饰器
    def spam(x,y,z):
        print(x,y,z)
 1 print(type(Foo.spam)) #类型本质就是函数
 2 Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数
 3 
 4 f1=Foo()
 5 f1.spam(3,3,3) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制
 6 
 7 '''
 8 <class 'function'>
 9 3
10 3
11 '''

 应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了

 

 1 class Date:
 2     def __init__(self,year,month,day):
 3         self.year=year
 4         self.month=month
 5         self.day=day
 6     @staticmethod
 7     def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
 8         t=time.localtime() #获取结构化的时间格式
 9         return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
10     @staticmethod
11     def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
12         t=time.localtime(time.time()+86400)
13         return Date(t.tm_year,t.tm_mon,t.tm_mday)
14 
15 a=Date('1987',11,27) #自己定义时间
16 b=Date.now() #采用当前时间
17 c=Date.tomorrow() #采用明天的时间
18 
19 print(a.year,a.month,a.day)
20 print(b.year,b.month,b.day)
21 print(c.year,c.month,c.day)

 

 

 

类方法(classmethod)         

类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法。

 1 class A:
 2     x=1
 3     @classmethod
 4     def test(cls):
 5         print(cls,cls.x)
 6 
 7 class B(A):
 8     x=2
 9 B.test()
10 
11 '''
12 输出结果:
13 <class '__main__.B'> 2
14 '''
 1 import time
 2 class Date:
 3     def __init__(self,year,month,day):
 4         self.year=year
 5         self.month=month
 6         self.day=day
 7     @staticmethod
 8     def now():
 9         t=time.localtime()
10         return Date(t.tm_year,t.tm_mon,t.tm_mday)
11 
12 class EuroDate(Date):
13     def __str__(self):
14         return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
15 
16 e=EuroDate.now()
17 print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
18 '''
19 输出结果:
20 <__main__.Date object at 0x1013f9d68>
21 '''

 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod。

 

 1 import time
 2 class Date:
 3     def __init__(self,year,month,day):
 4         self.year=year
 5         self.month=month
 6         self.day=day
 7     # @staticmethod
 8     # def now():
 9     #     t=time.localtime()
10     #     return Date(t.tm_year,t.tm_mon,t.tm_mday)
11 
12     @classmethod #改成类方法
13     def now(cls):
14         t=time.localtime()
15         return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化
16 
17 class EuroDate(Date):
18     def __str__(self):
19         return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
20 
21 e=EuroDate.now()
22 print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
23 '''
24 输出结果:
25 year:2017 month:3 day:3
26 '''

强调,注意注意注意:静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的。

 

 

———————————————————————————————————————————

 

 

Though classmethod and staticmethod are quite similar, there's a slight difference in usage for both entities: classmethod must have a reference to a class object as the first parameter, whereas staticmethod can have no parameters at all.

 1 Example
 2 
 3 class Date(object):
 4 
 5     def __init__(self, day=0, month=0, year=0):
 6         self.day = day
 7         self.month = month
 8         self.year = year
 9 
10     @classmethod
11     def from_string(cls, date_as_string):
12         day, month, year = map(int, date_as_string.split('-'))
13         date1 = cls(day, month, year)
14         return date1
15 
16     @staticmethod
17     def is_date_valid(date_as_string):
18         day, month, year = map(int, date_as_string.split('-'))
19         return day <= 31 and month <= 12 and year <= 3999
20 
21 date2 = Date.from_string('11-09-2012')
22 is_date = Date.is_date_valid('11-09-2012')

 

Explanation

Let's assume an example of a class, dealing with date information (this is what will be our boilerplate to cook on):

1 class Date(object):
2 
3     def __init__(self, day=0, month=0, year=0):
4         self.day = day
5         self.month = month
6         self.year = year

 This class obviously could be used to store information about certain dates (without timezone information; let's assume all dates are presented in UTC).

Here we have __init__, a typical initializer of Python class instances, which receives arguments as a typical instancemethod, having the first non-optional argument (self) that holds reference to a newly created instance.

 

Class Method

We have some tasks that can be nicely done using classmethods.

Let's assume that we want to create a lot of Date class instances having date information coming from outer source encoded as a string of next format ('dd-mm-yyyy'). We have to do that in different places of our source code in project.

So what we must do here is:

  1. Parse a string to receive day, month and year as three integer variables or a 3-item tuple consisting of that variable.
  2. Instantiate Date by passing those values to initialization call.

This will look like:

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

 

For this purpose, C++ has such feature as overloading, but Python lacks that feature- so here's when classmethod applies. Lets create another "constructor"

1 @classmethod
2     def from_string(cls, date_as_string):
3         day, month, year = map(int, date_as_string.split('-'))
4         date1 = cls(day, month, year)
5         return date1
6 
7 date2 = Date.from_string('11-09-2012')

 Let's look more carefully at the above implementation, and review what advantages we have here:

  1. We've implemented date string parsing in one place and it's reusable now.
  2. Encapsulation works fine here (if you think that you could implement string parsing as a single function elsewhere, this solution fits OOP paradigm far better).
  3. cls is an object that holds class itself, not an instance of the class. It's pretty cool because if we inherit our Date class, all children will have from_string defined also.

 

 

Static method

What about staticmethod? It's pretty similar to classmethod but doesn't take any obligatory parameters (like a class method or instance method does).

Let's look at the next use case.

We have a date string that we want to validate somehow. This task is also logically bound to Dateclass we've used so far, but still doesn't require instantiation of it.

Here is where staticmethod can be useful. Let's look at the next piece of code:

1     @staticmethod
2     def is_date_valid(date_as_string):
3         day, month, year = map(int, date_as_string.split('-'))
4         return day <= 31 and month <= 12 and year <= 3999
5 
6     # usage:
7     is_date = Date.is_date_valid('11-09-2012')

 

So, as we can see from usage of staticmethod, we don't have any access to what the class is- it's basically just a function, called syntactically like a method, but without access to the object and it's internals (fields and another methods), while classmethod does.

 

 

本文来自于:

python基础----特性(property)、静态方法(staticmethod)、类方法(classmethod)、__str__的用法

知乎用户fosmjo

stackoverflow用户crizCraig

感谢!!!

 

posted @ 2018-04-24 14:06  寒杰士  阅读(412)  评论(0编辑  收藏  举报