编译型语言和解释型语言
- 编译型语言
- 定义
需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言。一般需经过编译(compile)、链接(linker)这两个步骤。- 编译是把源代码编译成机器码
- 链接是把各个模块的机器码和依赖库串连起来生成可执行文件
- 优缺点
- 优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。
- 缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
- 代表语言
C、C++、Pascal、Object-C以及最近很火的苹果新语言swift
- 定义
- 解释型语言
- 定义
解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。 - 优缺点:
- 优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
- 缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
- 代表语言
JavaScript、Python、Erlang、PHP、Perl、Ruby
- 定义
动态语言和静态语言
- 静态类型语言
- 定义
类型判断是在运行前判断(如编译阶段),比如C#、java就是静态类型语言,静态类型语言为了达到多态会采取一些类型鉴别手段,如继承、接口 - 优缺点
优点:结构非常规范,便于调试,方便类型安全;缺点:为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。
- 定义
- 动态语言
- 定义
程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化,类型的检查是在运行时做的。 - 优缺点
优点:方便阅读,不需要写非常多的类型相关的代码;缺点:不方便调试,命名不规范时会造成读不懂,不利于理解等。
- 定义
- 关于弱类型、强类型、动态类型、静态类型语言的划分
使用__slots__
限制实例的属性
- 动态语言动态绑定属性和方法
当定义了一个class,创建实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性,绑定方式及效果请看以下代码
对实例绑定的属性或方法只对绑定实例有效,对另外实例无效。采用给class绑定,则对所有实例均有效。
class Student:
pass
s1 = Student()
s1.name = 'lz'
print(s1.name) # lz
s2 = Student()
# print(s2.name) # 报错:AttributeError: 'Student' object has no attribute 'name'
def set_age(self, age):
self.age = age
from types import MethodType
s1.set_age = MethodType(set_age, s1)
s1.set_age(17)
print(s1.age) # 17,对s2不起作用
# 对实例绑定的属性或方法只对绑定实例有效,对另外实例无效。采用给class绑定,则对所有实例均有效。
Student.set_age = MethodType(set_age ,Student)
s3 = Student()
s3.set_age(20)
print(s3.age) # 20
- 使用__slots__限制实例的属性
如果想要限定实例的属性,可以使用__slots__对类属性进行限制,但是只对当前类的实例有效,对其子类无效。
class Fish:
__slots__ = ('name', 'age', 'sex') # 如果这里没有sex,则下面的print会报错没有该属性
def __init__(self, sex):
self.sex = sex
f1 = Fish('man')
print(f1.sex)
# f1.size = 18 # 报错没有该属性
@property
@property广泛应用于类的定义中,可以简化代码,同时保证对参数进行必要的检查,检查程序运行的出错。
在Python基础(十)—面向对象的深入讲解(继承、Mixin编程机制等)章节中,说的是property(fget=None, fset=None, fdel=None, doc=None):通过类定义的属性设置属性。和@property有本质区别.
- 范围限定
例如我们新建Student,在设置成绩sorce属性上,我们是要对sorce进行范围限定的,这个就需要在class中的set方法中进行相对的限定,如下代码:
class Student:
def get_sorce(self):
return self.sorce
def set_sorce(self, value):
if not isinstance(value, int):
raise ValueError('sorce必须是Int')
if value < 0 or value > 100:
raise ValueError('sorce必须介于0~100')
self._sorce = value
单下划线和双下划线都是限定属性的私有
- 使用@property装饰器讲方法变为属性
@property可以使方法变为属性的方式进行访问,以上代码修改为:
class Student:
@property
def sorce(self):
return self.sorce
@sorce.setter
def set_sorce(self, value):
if not isinstance(value, int):
raise ValueError('sorce必须是Int')
if value < 0 or value > 100:
raise ValueError('sorce必须介于0~100')
self._sorce = value
#把一个getter方法加上@property,就变成了属性,同时@property本身又创建了另一个装饰器@sorce.setter,负责把一个setter方法变成属性赋值,这样就拥有了一个可控的属性操作。
- 利用@property进行属性的读写限制
如果之定义getter方法,而不定义setter方法,那该属性则是只读的,从以下方法可看出,age不需要可写,只需当前时间减去birth即可,因此设置为只读。
class Man:
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2019-self._birth
}
使用枚举类
- 使用枚举类的好处
对于一些常量(星期,月份等),使用不可变的枚举类,即增加了可读性,也不用担心出错,并且枚举类在内存中存放的是数字,也提高了程序效率,同时成员还可以直接比较。 - 创建枚举类
枚举类型可以通过继承Enum类来实现,注意Enum类是在enum模块中的。
from enum import Enum
class VIP(Enum):
YELLOW = 1
RED = 2
BLUE = 3
```
* 查看枚举类型
枚举类型是一个特殊的类,我们可以查看它的名称和值。
```javascript
print(VIP.YELLOW) #枚举类型
print(VIP['YELLOW']) #枚举类型
print(VIP.YELLOW.name) #枚举名称
print(VIP.YELLOW.value) #枚举值
print(VIP(3)) #数字得到枚举类型
#遍历
for v in VIP: #遍历
print(v)
# 成员的value默认从1开始
for name, member in VIP.__members__.items():
print(name, "->", member, ',', member.value)
"""
YELLOW -> VIP.YELLOW , 1
RED -> VIP.RED , 2
BLUE -> VIP.BLUE , 3
"""
- 重复和唯一的枚举类型
当存在枚举成员的名称有重复时,则后面的枚举成员相当于第一个枚举成员的别名,而且在实际使用中,就是使用的第一次出现的枚举成员。
from enum import Enum, unique
@unique
class Mistake(Enum):
ONE = 1
TWO = 2
THREE = 3
FOUR = 3
"""
出错:ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
"""
- 枚举类型的比较
枚举类型不能做大小比较,但是可以做身份比较和等值比较。枚举类型没有定义比较运算符,通常不能进行大小比较(不过,继承“IntEnum"类的枚举类型可以进行大小比较,他们的枚举值只能是整数)。
枚举类型是使用单例模式实现的。在创建枚举类的时候,Python就在内存中为我们创建了枚举类的对象,因此我们不必实例化枚举类。并且由于枚举类的“new”方法,将会保证内存中只会存在一个枚举类的实例。
type()创建类
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是在运行时动态创建的。
- 假如我们定义了Hello的class,并保存为hello.py.当Python解释器载入hello.py模块时,会依次执行该模块的所有语句,执行的结果就是动态地创建出一个hello的class对象,测试如下(type()可以查看一个类型或变量的类型):
class Hello(object):
def hello(self, name='world'):
print('hello %s.' % name)
h = Hello()
print(type(Hello)) # <class 'type'>
print(type(h)) # <class '__main__.Hello'>
# Hello是一个class,类型为type,h是一个实例,它的类型就是class Hello
- type()不仅可以查看一个对象的类型,还可以使用type()函数创建一个class,以下使用type()创建上面的Hello类,无需使用class
def hl(self, name='world'):
print('hello %s.' % name)
Hello = type('Hello', (object,), dict(hello = hl)) # 创建Hello的类
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
"""
hello world.
<class 'type'>
<class '__main__.Hello'>
"""
- 使用type()创建类的三个参数介绍
- class的名称;
- 继承的父类集合:tuple的格式(单元素写法),支持多重继承;
- class的方法名称与函数绑定
通过type()函数创建类与直接写class是完全一样的,因为Python解释器遇到class定义时,仅是扫描class定义的语法,然后调用type()进行创建class,这也就是动态语言支持运行期间创建类。
metaclass 元类
- metaclass的解释
- 当我们定义了类之后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例
- 如果我们想创建出类,那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类
- 因此连接起来就是:先定义metaclass,就可以创建类,然后创建实例。
所以 metaclass 允许你创建类或修改类,也就是你可以把类看成是 metaclass 创建出来的“实例”。
- 实际运用很少,后续略…
个人博客:Loak 正 - 关注人工智能及互联网的个人博客
文章地址:Python基础(十二)—面向对象拾遗(_slots_、@property、枚举类、元类)