Python笔记_第四篇_高阶编程_实例化方法、静态方法、类方法和属性方法概念的解析。

1.先叙述静态方法

  我们知道Python调用类的方法的时候都要进行一个实例化的处理。在面向对象中,一把存在静态类,静态方法,动态类、动态方法等乱七八糟的这么一些叫法。其实这些东西看起来抽象,但是很好理解。这里面有一个难点就是静态方法的理解,我们先叙述这个概念。

  比如在C#语言中,我们一般在在一个类前面加上类似于staci这样的关键字

  public 类名{

    static void 方法(){

  

    }

  }

  一般这个时候方法可以直接用“类名.方法名”的方式直接调用类中的方法。其实这种方法叫做直接引用的方法,不需要通过其他的"new"一个实例化去引用。

  为什么会存在这么两种方法去取调用类中的方法属性呢?其中有这么一些原因和误解:

  * "静态方法常驻内存,实例化方法不是,所以静态方法效率高但占内存"?

    事实上,他们都是一样的,在加载时机和占用内存上,静态方法和实例化方法是一样的,在类型第一次被使用时加载。调用的速度基本上没有差别。

  * "静态方法在对上分配内存,实例化方法在堆栈上"?

    事实上所有的方法都不可能在堆或者栈上分配内存,方法作为代码是被加载到特殊的代码内存区域,这个内存区域是不可写的。

    方法占不占更多的内存和他是不是“静态”没有什么关系。

    因为字段使用阿里存储每个实例对象的信息的,所以字段会占有内存,并且因为每个实例对象的状态都不一致(至少补鞥呢认为它们是一致的),所以每个实例对象的所有字段都会在内存中有一份拷贝,也因为这样拟采用它们来区分现在操作的那个对象。但是方法不一样,不论有多少实例对象,它们的方法和代码都是一样的,所以只要有一份代码就高了。因此无论是静态还是非静态的方法都只存在一份代码,也就是只占用一份内存空间。

    同样的代码,为什么运行起来表现不一样?这就依赖于方法所用的数据了,只要有两种数据来源,一种是通过方法的参数传进来,另一种就是使用class成员变量的值。(这句话非常重要,后面代码会说明这个)

  * “实例方法需要先创建实例才可以调用,比较麻烦静态方法不用,比较简单”?

    事实上如果一个方法与他所在类的实例对象无关,那么他就应该是静态的,而不应该把它写成实例方法。所以所有的实例方法都与实例有关,既然与实例有关,那么创建实例就是必然的步骤,没有麻烦简单一说。当然你可以完全把所有的实例方法都写成静态的,将实例作为参数传入即可,一般情况下不会出什么问题。从面向对象的角度来说,在抉择使用实例化方法还是静态方法时,应该根据是否该方法和实例化对象具有逻辑上的相关性,如果是!就应该使用实例化对象,反之使用静态方法。这是从面向对象角度上来说的。如果从线程安全,性能,兼容性上来看,也是选用实例化方法为宜。

  * “为什么我们要把方法区分为:静态方法和实例化方法?”

    如果我们要继续深入研究的话,就要脱离技术谈理论了。早期的结构化编程,几乎所有的方法都是“静态方法”,引入实例化方法概念是面向对象概念出现之后的事情了。区分静态方法和实例化方法不能单单从性能上去立即,创建C++、java、C#这样面向对象语言的大师引入实例化方法一定不是为了解决什么性能,内存问题,而是为了让开发更加模式化、面向对象。这样说的话,静态方法和实例化方法的区分是为了解决模式问题。

  举例说明:

  比如“人”这个类,每个人都有姓名、年龄、性别、身高等,这些属性就应该是非静态的,因为每个人都是的这些属性都不相同;但人在生物学上属于哪个部门哪个纲那么目等,这些属性属于整个人类,这就应该是静态的--他不依赖某个特定的人。

2. 关于装饰器的再理解

  装饰器我们前面用到过可以在不改变函数本身的情况下,可以通过外部的更改对整个函数的内容进行“添加”某些参数,方法等。我们发现像C#这样的语言没有装饰器这么一说,但是像C#这样语言如果想定义一个静态方法前面要添加一个static这样的变量。这样我们就是能再更加深入的理解Python这个装饰器其实就是把一些需要进行“特性变更”的部分,单独拿出来作为一种单独的“功能”,对需要特性变更的部分进行“装饰”、“装修”、“安装”,是对面向对象概念的更进一步的提升(其实学过其他面向对象语言的人觉得Python当中怎么没有静态方法这些东西,其实Python把他们进行再进一步提炼,用一个装饰器来作为一个工具去使用了,有时候觉得怪怪的而已)。

3. 四个概念的排列

  实例化方法:在类中,第一个参数必须是实例对象,该参数名一般约定为“self”,通过他来传递实例的属性和方法。也可以传递类的属性和方法。注意:这里的self可以用其他传参来代替,但是约定为self,且为了区分静态方法,这里的传参必须要写上self。实例化的格式:实例化变量 = 类名()。

        调用:只能由实例对象调用。

        装饰器:无装饰器

  静态方法:千万不要写“self”和“cls”参数。其他参数随意,用于区分实例化方法和类方法。但是方法中不能使用类或实例的任何属性和方法。

        调用:实例对象和类对象都可以调用。为了区分笔建议直接用类调用:类名.实例化方法。

        装饰器:@staticmethod

  类方法:第一个参数必须是当前类对象,该参数名一般约定为“cls”参数,建议写上“cls”,通过他来传递类的属性和方法(但是不能传递实例的属性和方法)

        调用:实例对象和类对象都可以调用。

        装饰器:@classmethod

  属性方法(静态属性方法):属性方法这里指的是静态属性方法。调用可以直接.点的方式调用,调用不能有()。否则会报错。属性方法是可以把个类当中的属性通过方法的方式进行静态的调用。

        调用:如果只对本类的属性进行静态处理不需要用实例化方法,如果通过方法去更改属性值且再调用这个方法需要实例化方法。

        装饰器:@property 和 @属性/方法.setter

4. 分别举例说明

  4.1 实例化方法(略)

  4.2 静态方法:

import time

class TimeTest(object):
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S", time.localtime())


print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)

  备注:注意到staticmethod里面是没有参数的,但是可以有参数,就是不能有self和cls参数,我们看到可以直接静态方法调用,也可以实例化方法调用。

  4.3  类方法:

class ClassTest(object):
    __num = 0

    @classmethod
    def addNum(cls):
        cls.__num += 1

    @classmethod
    def getNum(cls):
        return cls.__num

    # 这里我用到魔术方法__new__,主要是为了在创建实例的时候调用累加方法。
    def __new__(self):
        ClassTest.addNum()
        return super(ClassTest, self).__new__(self)


class Student(ClassTest):
    def __init__(self):
        self.name = ''

a = Student()
b = Student()
print(ClassTest.getNum())

  备注:其实我们这里看到了,类方法修饰类的_属性。注意这里添加的cls。我们用静态类方法可以直接调用。

  4.4 属性方法:

class Person(object):
    def __init__(self,name):
        self.name = name
        self.food = None

    @property
    def eat(self):
        print("%s 正在吃 %s" %(self.name,self.food))

    @eat.setter
    def eat(self,food):
        self.food = food


p1 = Person("Thomas")
p1.eat = "meat"
p1.eat
# Thomas 正在吃 meat
class Person(object):
    def __init__(self,age):
        # 属性直接对外暴露,改为限制访问的方式
        self.__age = age
    # 方法名为受限制的变量去掉双下划线的方式访问
    @property      # 其实也是一种装饰器 这个就是get方法的意思
    def age(self):
        return self.__age
    @age.setter    # 去掉下划线.setter 这个就是set方法的意思
    def age(self,age):
        if age < 0:
            age = 0
        else:
            self.__age = age

print(Person(19).age)
# 19

  备注:我们通过第一个例子得到,我们用一个方法调用属性后再通过这个方法赋值,只能用实例的方式,如果我们用这个方法只是去修改属性值。可以直接把属性值点出来。后面那个例子在get和set的方式里面我们提到过。

原文地址:https://www.cnblogs.com/noah0532/p/10926966.html