python学习笔记3--类方法、装饰器入门

类定义中的静态方法和类方法

静态方法

  • 定义时需加修饰器@staticmethod,入口参数中无self;
  • 可以同过实例两种形式调用,多用类调用;
  • 可以看作是类内部的“外界普通函数”,不继承类内任何信息,用法上与外界函数完全一样;
  • 不能直接访问类中的方法或变量;(不继承任何信息)
  • 但是可以调用实例化后的方法和属性,或者加了类前缀的;但此时已经不是“类内部调用”了,已经和外界函数调用类的信息一样了;
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,表示类本身而不是实例对象;
  • 可以同过实例两种形式调用,效果完全相同;
  • 继承类自身的静态属性和静态方法,但不能直接访问实例属性和方法;
  • 同静态方法,可以调用实例化后的实例方法和属性;
    # 定义类方法
    @classmethod
    # 这里cls是类的元数据,不是类的实例,指的是类本身
    def do(cls):
        print(cls)	# ,指的是类本身
        print('[', cls.name, ']')   # 可以访问类中的静态变量
        cls.run()                   # 可以访问类中的静态方法
        # print(cls.value)     # 同样不能调用实例方法中的值
        print(cls().value)		# 但将类实例化后又可以调用了

这里能看出,在类的内部也是可以调用类自身或者类的实例化的(套娃)。

实例方法

  • 入口参数为self,表示实例对象而不是类本身;
  • 必须通过实例调用,不能通过类调用;
  • 可以调用类内一切的属性和方法(一切静态、类、实例都可以)
    # 定义实例方法,实例方法可以访问一切
    def do1(self):
        print(self.value)	# 可以访问类的静态属性name
        print('<', self.name, '>')
        print(self)		# <__main__.Myclass object at 0x0000011E48F1C9B0>
       				    # self值得是实例对象而不是类本身

总结:可以看出类方法和静态方法在使用上的最大区别就是,类方法可以直接访问类内静态方法和属性,而静态方法连这个都不可以。

装饰器@property,类的getter和setter

@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置于要装饰的函数定义前,构成新的函数。在调用时依旧只需要调用“老函数”(不需加上装饰器)即可,但在实际函数内容上已经变成装饰后的了。

__slots__元组

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'

要注意的是:

  1. 元组内存储的是“属性名”,是字符串而不是属性本身,要加引号;
  2. __slots__只适用于改来本身,不适用于继承它的子类。

抽象类,装饰器@abstractmethod和ABCMeta元类

抽象类的意思是该类不可被实例化为对象,而只能供子类继承,起到一个提供框架的作用。
只要类中的存在一个“抽象方法”,则该类就为抽象类。抽象方法起一个“占位”的作用,表示子类都有这个方法,但具体形式各不相同(多态)。

抽象类的定义:要在定义时继承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包装器来达到抽象类的效果。

你可能感兴趣的:(python)