Python核心编程笔记--动态属性
Python核心编程笔记–动态属性
一、动态语言与静态语言
1.1 静态语言特点:
a. 在定义变量时需要指定变量的类型,根据指定的类型来确定变量所占的内存空间
b. 需要经过编译才能运行
c. 在代码编译后,运行过程不能对代码进行操作
d. 常见的静态语言:C、C++、Java等
1.2 动态语言的特点:
a. 不需要经过编译,而是由解释器程序来解释执行代码
b. 在代码运行过程中,可以动态地对代码进行操作
c. 常见的动态语言:Python、PHP、Ruby、JavaScript等
1.3 优缺点比较:
a. 静态语言会声明变量类型,可以帮助计算机在执行代码前来发现更多潜在的错误,但这样会需要使用者进行更多的思考和编码。反之动态语言则不需要,它很灵活
b. 静态语言在编译后,执行速度一般是大于动态语言的
c. 静态语言结构比较规范,能很方便调试,但需要与大量类型相关的代码。而动态语言不需要与大量类型相关的代码,但如果编写不规范,不容易调试
二、Python的动态添加
2.1 动态添加属性
首先,我们定义一个简单的Person类,它所有姓名和年龄两个属性。并声明一个它的实例对象p。给p动态添加一个属性sex。代码如下所示:
class Person(object): def __init__(self, name, age): self.name = name #姓名 self.age = age #年龄 p = Person('Demon', 18) print(p.name) #Demon print(p.age) #18 p.sex = 'M' #对对象p动态添加属性sex,并赋值为M print(p.sex) #M p2 = Person('Semon', 18) #声明另一个对象p2 # print(p2.sex) #报错:AttributeError: 'Person' object has no attribute 'sex'
从上面的代码可以看出,我们可以直接通过 “对象.新变量名 = 新变量值” 的形式来给对象动态添加一个属性(通过dir(对象名)可以查看当前拥有的属性)。同时当我们重新创建一个新的对象p2,想要调用刚刚动态添加的属性sex时,发现报错了。这说明我们动态创建的属性是只针对当前对象而言的,并不会影响其他对象的使用。这也称之为给对象动态添加属性。
那么,如果我们想要动态添加一个属性,这个属性对所有对象都能生效应该怎么做呢?即我们需要给类动态添加属性,代码如下所示:
class Person(object): def __init__(self, name, age): self.name = name #姓名 self.age = age #年龄 p = Person('Demon', 18) #声明一个对象p p2 = Person('Semon', 18) #声明另一个对象p2 Person.addr = 'SS' #给类动态添加属性 print(p.addr) # SS print(p2.addr) # SS
2.2 __slots__的使用
简单来说,__slots__的作用就是规定该类能使用的属性是哪些。
import types class Person(object): def __init__(self, name, age): # self.name = name #姓名,在init中定义了name,会报错:AttributeError: 'Person' object has no attribute 'name' self.age = age #年龄 __slots__ = ('age', 'sex') #而在__slots__中没有定义name,则在init中去使用name会报错 p = Person('Demon', 18) #声明一个对象p # print(p.name) # AttributeError: 'Person' object has no attribute 'name' def eat(self): print('----%s is eating' % self.name) # p.eat = types.MethodType(eat, p) #AttributeError: 'Person' object has no attribute 'eat'
官方文档中规定__slots__可接受一个字符串、可迭代的类型比如列表,元组等。而且父类中定义的__slots__,在子类中是不生效的。而解决办法就是在子类中自己定义自己的__slots__,并包含父类的定义即可。示例代码如下:
import types class Person(object): __slots__ = 'name' class Student(Person): #__slots__ = ['name', 'age'] #解决办法是子类自己定义 pass p = Person() p.name = 'Demon' #p.age = 18 #AttributeError: 'Person' object has no attribute 'age' s = Student() s.name = 'Demon' s.age = 18 #子类可以添加
2.3. 动态添加方法
首先,我们从上面已知的属性动态添加的方法进行分析,其实在python中,类并没有方法的概念,它有的都是属性,当我们调用比如p.eat(),其实是先通过p.eat找到一个调用,然后通过(),来执行调用。为了方便我们一般称p.eat()这种形式的称为调用方法(具体后面会再写笔记进行介绍)。既然是这样,我们会想当然的认为可以通过上面介绍的动态添加属性的方式来添加方法,我这样想,也就这样做了,代码如下所示:
class Person(object): def __init__(self, name, age): self.name = name #姓名 self.age = age #年龄 p = Person('Demon', 18) #声明一个对象p p2 = Person('Semon', 18) #声明另一个对象p2 Person.addr = 'SS' #给类动态添加属性 print(p.addr) # SS print(p2.addr) # SS def eat1(): print("----eat no self method-----") def eat2(self): print("----eat has self method---- %s" % self.name) p.eat = eat1 #给对象动态指定一个方法 p.eat() # ----eat no self method----- # p2.eat() # AttributeError: 'Person' object has no attribute 'eat' p.eat = eat2 # p.eat() #TypeError: eat2() missing 1 required positional argument: 'self' Person.eat = eat1 Person.eat() #----eat no self method----- # p2.eat() # TypeError: eat1() takes 0 positional arguments but 1 was given Person.eat = eat2 #给类动态指定一个方法 p2.eat() #----eat has self method---- Semon
可以看到,通过对象.的方法动态添加方法,应该说只能实现部分的效果,就上述代码的运行情况,我们可以总结如下:
a. 如果添加一个不带self参数的方法,可以直接通过 实例.方法名 的方式添加,也可以通过 类名.方法名添加
b. 如果添加一个带self参数的方法,则只能通过类名.方法名来添加
而从另一个角度,我们都知道,在Python中,类的方法可以分为以下三种:
实例方法(instance method):被类实例调用,第一个参数默认是self
静态方法(static method):可以被类和实例调用,没有默认参数
类方法(class method):可以被类和实例调用,第一个参数默认是类,一般用cls变量名