元类
类就是一组用来描述如何生成一个对象的代码段。但是Python中的类还远不止如此,它的类同样也是一种对象,当使用关键字class时,Python解释器在执行的时候就会创建一个对象。
class ObjectCreator(object): #创建了一个名为ObjectCreator的对象
pass #这个对象拥有创建对象(实例对象)的能力
print(ObjectCreator) #可以打印一个类
def echo(o):
print(o)
echo(ObjectCreator) #可以将类做为参数传给函数
ObjectCreator.new_attribute = 'foo' #可以为类增加属性
print(ObjectCreator.new_attribute)
ObjectCreatorMirror = ObjectCreator #可以将类赋值给一个变量
print(ObjectCreatorMirror)
#outputs:
#<class '__main__.ObjectCreator'>
#<class '__main__.ObjectCreator'>
#foo
#<class '__main__.ObjectCreator'>
可以将它赋值给一个变量;可以拷贝它;可以为它增加属性;可以将它作为函数参数进行传递
动态地创建类:类也是对象,可以在运行时动态的创建它们,就像其他任何对象一样
def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar
MyClass = choose_class('foo')
print(MyClass) # 函数返回的是类,不是类的实例
print(MyClass()) # 可以通过这个类创建类实例,也就是对象
使用type创建类:type可以接受一个类的描述作为参数,然后返回一个类。
#type(类名, 由父类名称组成的元组,包含属性的字典)
class Test:
pass
print(Test()) #创建了一个Test类的实例对象;
#output:<__main__.Test object at 0x0000022E319D9860>
Test2 = type("Test2",(),{}) #定了一个Test2类
print(Test2()) #创建了一个Test2类的实例对象
MyDogClass = type('MyDog', (), {})
print(MyDogClass) #output:<class '__main__.MyDog'>
Foo = type('Foo', (), {'bar':True}) #使用type创建带有属性的类Foo
print(Foo,',',Foo.bar) #outputs:<class '__main__.Foo'> , True
FooChild = type('FooChild', (Foo,),{}) #类继承
print(FooChild,',',FooChild.bar) #添加的属性是类属性,并不是实例属性
def echo_bar(self): #定义了一个普通的函数
print(self.bar)
#让FooChild类中的echo_bar属性,指向了上面定义的函数
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
my_foo = FooChild()
my_foo.echo_bar() #output:True
元类:元类就是用来创建类的“东西”。元类就是用来创建这些类(对象)的,元类就是类的类。函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。
Python中所有的东西,注意,我是指所有的东西——都是对象。包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。
元类就是创建类这种对象的东西。type就是Python的内建元类
__metaclass__属性:在定义一个类的时候为其添加__metaclass__属性,Python就会用元类来创建类
Python动态语言
在运行时可以改变其结构的语言 ,例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。
动态语言:可以在运行的过程中,修改代码
静态语言:编译时已经确定好代码,运行过程中不能修改
1 运行的过程中给对象绑定(添加)属性
class Person(object):
def __init__(self, name = None, age = None):
self.name = name
self.age = age
P = Person("小明", "24")
P.sex = "male" # 动态给实例绑定属性
2 运行的过程中给类绑定(添加)方法
import types
class Person(object):
def __init__(self, name = None, age = None):
self.name = name
self.age = age
def eat(self):
print("eat food")
def run(self, speed): #定义在类外的函数
print("%s is running, his speed is %d km/h"%(self.name, speed))
P = Person("hill", 24) #实例
P.eat() #output:eat food
#P.run() #Error直接添加出错
P.run = types.MethodType(run, P) #使用types模块添加实例方法
P.run(100) #output:hill is running, his speed is 100 km/h
语法:types.MethodType ( function, instance )
对于静态方法:可以直接绑定并调用
@staticmethod
def testStatic():
print("---static method----")
Person.testStatic = testStatic
Person.testStatic()
对于类方法::也可以直接绑定和调用
@classmethod
def testClass(cls):
print("---class method----")
cls.num = 100
Person.testClass = testClass
print(Person.num)
Person.testClass()
__slots__的作用
Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Person(object):
__slots__ = ("name", "age") #此时只能添加这两种属性
import导入模块:
按以下路径搜索
import sys
sys.path
添加路径:sys.path.append(‘/路径名’)
如:
['',
'E:\\Anaconda3\\python36.zip',
'E:\\Anaconda3\\DLLs',
'E:\\Anaconda3\\lib',
'E:\\Anaconda3',
......]
重新导入模块(导入模块之后如果模块发生修改,则无法获取新的内容),需要reload:
from imp import *
reload(模块名)
循环导入:几个模块之间相互重复调用,可以另建一个主模块分别导入子模块使用。
作用域
命名空间、局部变量、全局变量
LEGB 规则:
locals -> enclosing function -> globals -> builtins
locals:当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
enclosing:外部嵌套函数的命名空间(闭包中常见)
globals:全局变量,函数定义所在模块的命名空间
builtins:内建模块的命名空间
内建:Python 在启动的时候会自动载入很多内建的函数、类,比如 dict,list,type,print,这些都位于 __builtin__ 模块中
is和==
a=[1];b=[1]
a == b
a is b
c=a
print(a,b,c)
print(id(a),id(b),id(c))
输出为:
Ture
False
[1] [1] [1]
2160069216008 2160068308424 2160069216008
即:is用于比较两个引用是否指向同一个对象;==用于比较两个对象是否相等
深拷贝、浅拷贝
浅拷贝:指向同一块内存地址
a=[1];b=a
print(id(a),id(b))
a.append(2)
print(a,b)
输出:
2160069215432 2160069215432
[1, 2] [1, 2]
深拷贝:不同的存储1单元
import copy
a=[1]; c = copy.deepcopy(a)
print(id(a),id(c))
a.append(2)
print(a,c)
输出
2160069013448 2160069216136
[1, 2] [1]
deepcopy对嵌套的引用同样是另起一块内存来存储。(完全深拷贝)
copy.copy和copy.deepcopy的区分:copy.copy不能对嵌套的内容深拷贝
copy对不可变类型(如元组)不执行深拷贝。
进制转换
bin(10) #10→2
int("1001",2) #2→10
hex(10) #10→16
int('ff', 16) #16→10
bin(0xa) #16→2
hex(0b1001) #2→16
位运算
print(9&8,9&5,9|5,9^5,~9,~5)
结果:
8 1 13 12 -10 -6
私有化
xx: 公有变量
_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字
xx_:单后置下划线,用于避免与Python关键词的冲突
父类中属性名为: __名字 的,子类不继承,子类不能访问
如果在子类中向 __名字 赋值,那么会在子类中定义的一个与父类相同名字的属性
_名 的变量、函数、类在使用from xxx import *时都不会被导入
通过name mangling(名字重整(目的就是以防子类意外重写基类的方法或者属性)如:_Class__object)机制就可以访问私有。
property的使用
对私有属性,可以添加getter和setter方法来访问:
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self): #使用getMoney方法访问私有属性self.__money
return self.__money
def setMoney(self, value): #使用setMoney方法访问私有属性self.__money
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
使用property升级getter和setter方法
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self, value):
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
money = property(getMoney, setMoney) #property的使用
a = Money()
a.money #此时为0
a.money = 100 #相当于a.setMoney(100)
a.money #此时为100
a.getMoney() #100
print(a.money) #相当于调用了a.getMoney
使用装饰器 @property 成为属性函数,可以对属性赋值时做必要的检查
作用:将方法转换为只读、重新实现一个属性的设置和读取方法,可做边界判定
class Money(object):
def __init__(self):
self.__money = 0
@property
def money(self):
return self.__money
@money.setter
def money(self, value):
if isinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
垃圾回收
小整数对象池:小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收,即常驻于内存。在一个 Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象。单个字符共用对象,也常驻内存
大整数对象池:每一个大整数,均创建一个新的对象。
intern机制:单个单词(没有空格),不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁 ;字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁
Garbage collection(GC垃圾回收):python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
引用计数机制:python里每一个东西都是对象,它们的核心就是一个结构体:PyObject
typedef struct_object {
int ob_refcnt;
struct_typeobject *ob_type;
} PyObject;
它是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少。当引用计数为0时,该对象生命就结束了。
优点:简单、实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
缺点:维护引用计数消耗资源、循环引用
垃圾回收机制
导致引用计数+1的情况:1对象被创建,例如a=23;2对象被引用,例如b=a;3对象被作为参数,传入到一个函数中,例如func(a);4对象作为一个元素,存储在容器中,例如list1=[a,a]
导致引用计数-1的情况:1对象的别名被显式销毁,例如del a;2对象的别名被赋予新的对象,例如a=24;3一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会);4对象所在的容器被销毁,或从容器中删除对象
查看一个对象的引用计数:
import sys
a = "hello world" #比正常计数大1
sys.getrefcount(a) #因调用函数时传入a,让a的计数+1
循环引用导致内存泄露
触发垃圾回收:调用gc.collect();当gc模块的计数器达到阀值的时候;程序退出的时候