静态方法:
class Myclass(object): # 部分来自python进阶之路的博客
# 定义一个静态变量
name = 'Bill'
def __init__(self): # init中的变量只有实例化以后才会创建,直接调用类是不会有的
print('Myclass已被实例化')
# 定义实例变量,静态方法和类方法不能访问该变量,只有实例方法可以
self.value = 20
# 定义静态方法
@staticmethod
def run():
# 访问Myclass类中的静态变量name,但不能直接访问,需加类前缀Myclass.name;即静态方法不会继承类内的变量,需单独引用(与外界一般函数相同)
print('*', Myclass.name, '*')
# print(self.value) # run内没继承self,不能引用self.value
# print(Myclass.value) # 不实例化的类中也没有value
print(Myclass().value) # 实例化后可以调用value
类方法:
# 定义类方法
@classmethod
# 这里cls是类的元数据,不是类的实例,指的是类本身
def do(cls):
print(cls) # ,指的是类本身
print('[', cls.name, ']') # 可以访问类中的静态变量
cls.run() # 可以访问类中的静态方法
# print(cls.value) # 同样不能调用实例方法中的值
print(cls().value) # 但将类实例化后又可以调用了
这里能看出,在类的内部也是可以调用类自身或者类的实例化的(套娃)。
实例方法:
# 定义实例方法,实例方法可以访问一切
def do1(self):
print(self.value) # 可以访问类的静态属性name
print('<', self.name, '>')
print(self) # <__main__.Myclass object at 0x0000011E48F1C9B0>
# self值得是实例对象而不是类本身
总结:可以看出类方法和静态方法在使用上的最大区别就是,类方法可以直接访问类内静态方法和属性,而静态方法连这个都不可以。
@property
是一个装饰器,它的作用是可以把类中的“方法”通过“属性”的方式进行访问(读);与之相对的,@function_name.setter
可以把类中的“方法”通过属性的方式进行修改(写)。效果如下(来自骆峰的博客)
class Employee(object): # 来自骆峰的博客
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
employee = Employee('Sam')
print(employee.name) # Sam
employee.name = 'Amy'
print(employee.name) # Amy
这里的要求就是,对于@property
来说必须该方法只返回一个变量值,且命名无所谓,这个值就是读出的值;对于@function_name.setter
来说,需要在方法得入口加入且仅加入一个用于赋值的参数,命名无所谓。
如果需要对一个方法通过“属性”的方式既读又写,则需要把该方法在两中装饰器下各自定义一遍(且定义不同),如上所示。
为什么要这么做呢?为什么不直接调用或者写入属性反而要多此一举呢?
原因之一,便是因为对于直接写入属性来说,我们无法对输入的新值做出限制,直接写入会出现不合逻辑的情况(如负值年龄)。而通过方法定义的形式,我们很容易作出限制:
@age.setter
def age(self, age):
self._age = age if age > 0 else 0
如果输入值负值,就会保持self._age=0
。
当然还有其他原因,此处暂不表。对于通过运用@property
等装饰器,我们既可以方便的调用类内属性,又可以对属性的改写做出核验。
装饰器
多说一句,@peoperty是一种装饰器,property自身本质上是一种高阶函数(也是一种函数)。
@property
def name(self):
return self._name
实际上相当于
name = property(name)
原来的函数(方法)name作为参数输入进函数property中,然后再重新赋给name变量中。实际上此时的name已经不是原来的定义中的name函数了,而是重新指向了property(name)的新函数name了。
还有其他的各种功能的装饰器,调用方法都是@decorator_name
置于要装饰的函数定义前,构成新的函数。在调用时依旧只需要调用“老函数”(不需加上装饰器)即可,但在实际函数内容上已经变成装饰后的了。
python是一种动态语言,因为这在类之外的实例的使用中可以随实给该实例加入各种新方法和新属性。
__slots__元组的作用就是要限制这种行为,当类定义中出现__slots__时,类的实例就只能添加元组内规定的方法和属性了,添加任何额外的内容都会报错。
class Student(object): # 来自廖雪峰的博客
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "" , line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
要注意的是:
抽象类的意思是该类不可被实例化为对象,而只能供子类继承,起到一个提供框架的作用。
只要类中的存在一个“抽象方法”,则该类就为抽象类。抽象方法起一个“占位”的作用,表示子类都有这个方法,但具体形式各不相同(多态)。
抽象类的定义:要在定义时继承ABCMeta元类。
抽象方法的定义:在方法前加入@abstractmethod
装饰器。
@abstractmethod装饰器和ABCMeta元类都来自于abc模块,需要提前引入。(以下来自骆峰的博客)
from abc import ABCMeta, abstractmethod
class Pet(object, metaclass=ABCMeta):
"""宠物"""
def __init__(self, nickname):
self._nickname = nickname
@abstractmethod
def make_voice(self):
"""发出声音"""
pass
Python从语法层面并没有像Java或C#那样提供对抽象类的支持,只能通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果。