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) # 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__ = ['age'] # 只用定义 age , name 会默认合并进来
#解决办法是子类自己定义
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变量名
从之前的测试结果,不带参数的方法,可以通过.的方式来添加,类比如静态方法,我们可以猜测静态方法的动态添加也是通过 . 的方式,测试代码如下:
import types
class Person(object):
pass @staticmethod
def test_static():
print('aa') Person.xxxx = test_static # 测试返回值,不一定要命名为固定的名称,可以是任意的 Person.xxxx() # .后的名称与上面一致即可
同样,从我们预测的代码结论里,用类名.可以添加带self参数的方法,而类方法是默认带类对象的方法,我们也尝试用.的方式来添加,测试代码如下:
import types
class Person(object):
num = 0
pass @classmethod
def test_class(cls):
print(cls.num) # Person.xxxx = test_class # 测试返回值,不一定要命名为固定的名称,可以是任意的 Person.xxxx() # .后的名称与上面一致即可
综上两个测试代码可以得出,静态方法和类方法的动态添加和属性的动态添加是一样的实现方式。
而显然唯一不能通过.的方式进行动态添加的就是针对某个对象的实例方法。原因也很简单,实例方法必须第一个参数是实例对象,而方法是在类之后定义了,通过实例对象. 的方式去添加的时候,第一个参数找不着,所以会报错。解决方法是使用types模块中的MethodType(function, instance)。我们可以在IPython3中使用help来查看MethodType的使用文档:
import types help(types.MethodType) '''
class method(object)
| method(function, instance)
|
| Create a bound instance method object.
|
| Methods defined here:
|
| __call__(self, /, *args, **kwargs)
| Call self as a function.
|
| __delattr__(self, name, /)
| Implement delattr(self, name).
'''
可以看出,MethodType接受两个参数,第一个参数是需要绑定的函数名,第一个是实例对象。示例代码如下:
import types
class Person(object):
def __init__(self, name, age):
self.name = name #姓名
self.age = age #年龄 p = Person('Demon', 18) #声明一个对象p def eat(self):
print('----%s is eating' % self.name) p.eat = types.MethodType(eat, p)
p.eat()
这样我们就完成了实例方法的动态添加。注意使用MethodType需要定义返回,返回的是什么,就用什么来调用。
三、总结
最后总结结论如下:
1. 动态添加实例属性:使用 对象名.新变量名 的方式
2. 动态添加类属性:使用 类.新变量名 的方式
3. 限制类的属性:__slots__
4. 静态方法与类方法:使用 类.新方法 的方式
5. 实例方法:
如果只对某个对象生效:使用types模块中的对象名.新方法名 = MethodType(function, 对象名) 的方式
如果对所有对象生效:使用 类.方法名 的方式
Python核心编程笔记--动态属性的更多相关文章
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
- python核心编程--笔记(不定时跟新)(转)
的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v ...
- python核心编程笔记(转)
解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v 冗 ...
- Python核心编程笔记(类)
Python并不强求你以面向对象的方式编程(与Java不同) # coding=utf8 class FooClass(object): version = 0.1 def __init__(self ...
- Python核心编程笔记 第三章
3.1 语句和语法 3.1.1 注释( # ) 3.1.2 继续( \ ) 一般使用换行分隔,也就是说一行一个语句.一行过长的语句可以使用反斜杠( \ ) 分 ...
- Python核心编程笔记 第二章
2.1 程序输出:print语句 可以使用print语句显示变量的字符串表示,或者仅用变量名查看该变量的原始值. 2.2 程序输出和raw_input()内建函数 ...
- python核心编程笔记——Chapter7
Chapter7.映像和集合类型 最近临到期末,真的被各种复习,各种大作业缠住,想想已经荒废了python的学习1个月了.现在失去了昔日对python的触觉和要写简洁优雅代码的感觉,所以临到期末毅然继 ...
- python核心编程笔记
1.python的对象是通过引用传递的. 2.多元赋值: x, y = y, x
- Python核心编程笔记----注释
python 中注释有两种 第一种,文档注释 第二种,一般的注释 下面是例子: class MyClass: '这个是文档注释' def __repr__(self): return "re ...
随机推荐
- c++邻接表存储图(无向),并用广度优先和深度优先遍历(实验)
一开始我是用c写的,后面才发现广搜要用到队列,所以我就直接使用c++的STL队列来写, 因为不想再写多一个队列了.这次实验写了两个多钟,因为要边写边思考,太菜了哈哈. 主要参考<大话数据结构&g ...
- C# DropDownList 绑定枚举类
第一种 DropDownList_Franchiser_Type.DataSource = ListTypeForEnum(); DropDownList_Franchiser_Type.DataVa ...
- 解决failed to push some refs to git
Administrator@PC-20150110FGWU /K/cocos2d/yc (master) $ git push -u origin master To git@github.com:y ...
- JavaWeb学习总结(二)——Tomcat服务器学习和使用(一)(转)
转载自 http://www.cnblogs.com/xdp-gacl/p/3734395.html 一.Tomcat服务器端口的配置 Tomcat的所有配置都放在conf文件夹之中,里面的serve ...
- Java-----SSM(SpringMVC+Spring+mybaties)框架整合
在进行整合之前,首先了解这个框架的作用 Mybaties: 丰富的标签库,可写动态sql,并统一的在.XML文件中编写,方便统一管理,解耦 SpringMVC: 标准的MVC思想(mode,view, ...
- 湘潭大学1185 Bob's Problem
Bob's Problem Accepted : 114 Submit : 589 Time Limit : 1000 MS Memory Limit : 65536 KB 题目描写叙述 Bo ...
- 【Cocos游戏实战】功夫小子第七课之游戏主功能场景逻辑功能和暂停功能场景的分析和实现
CSDN的markdown编辑器是吃屎了么! !.什么玩意.!写了一半写不了东西还全没了,搞个毛线! 本节课的视频教程地址是:第七课在此 假设本教程有帮助到您,希望您能点击进去观看一下,并且如今注冊成 ...
- UML总结复习指南
用例图 1. 參与者(Actor) 表示与您的应用程序或系统进行交互的用户.组织或外部系统.用一个小人表示. 2. 用例(Use Case) 用例就是外部可见的系统功能,对系统提供的服务进行描 ...
- Spring之AOP实现面向切面编程
近期在学Java的动态代理和Spring面向切面编程,越来越认为Spring设计的真的是太完美了.于是,想一个最简单的样例来跑一下.但问题多多,显示缺少,Aspectj里面的相应的类.导入Aspect ...
- Python 项目实践一(外星人入侵)第二篇
接着上次的继续学习. 一 创建一个设置类 每次给游戏添加新功能时,通常也将引入一些新设置.下面来编写一个名为settings的模块,其中包含一个名为Settings的类,用于将所有设置存储在一个地方, ...