1 Python的函数参数传递例1

a = 1
def fun(a):
a = 2
fun(a)
print(a) # a = 1例2
a = []
def fun(a):
a.append(1)
fun(a)
print(a) # [1]

所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*。

通过id来看引用a的内存地址可以比较理解:

a = 1
def fun(a):
print("func_in",id(a)) # func_in 41322472
a = 2
print( "re-point",id(a), id(2)) # re-point 41322448 41322448
print("func_out",id(a), id(1)) # func_out 41322472 41322472
fun(a)
print(a) # 1

ps:具体的值在不同电脑上运行时可能不同。

可以看到,在执行完a = 2之后,a引用中保存的值,即内存地址发生变化,由原来1对象的所在的地址变成了2这个实体对象的内存地址。

而第2个例子a引用保存的内存值就不会发生变化:

a = []
def fun(a):
print("func_in",id(a)) # func_in 53629256
a.append(1)
print("func_out",id(a)) # func_out 53629256
fun(a)
print(a) # [1]

这里记住的是类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而 list, dict, set 等则是可以修改的对象。(重点)

当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.

2 Python中的元类(metaclass)

元类基本是不会用到的,这里只做了解

类即对象

python与在大多数编程语言一样:类就是一组用来描述如何生成一个对象的代码段。例1:

class Boy(object):
pass
fb = Boy()
print(fb)
print(id(fb)) # 140450144402232

而Python中的类还远不止如此。类同样也是一种对象。只要使用关键字class,Python解释器在执行的时候就会创建一个对象。

例1所示是将在内存中创建一个对象,名字就是Boy这个对象(类对象Boy)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,于是可以对它做如下的操作:将它赋值给一个变量

拷贝它

增加属性

将它作为函数参数进行传递

例2

# 可以打印一个类,因为它其实也是一个对象
In [5]: print(FatBoy)
In [6]:
# 可以将它作为函数参数进行传递
In [6]: def print_object(obj):
...: print(obj)
...:
In [7]: print_object(FatBoy)
In [8]:
# 可以为它增加属性
In [8]: FatBoy.hobby = "肥仔可乐水"
# 查看该属性是否存在
In [9]: print(hasattr(FatBoy,'hobby'))
True
# 打印该属性
In [10]: print(FatBoy.hobby)

肥仔可乐水

In [11]:
# 可以将它赋值给一个变量
In [11]: FatBoss = FatBoy
# 查看FatBoss是否是类
In [12]: print(FatBoss)
# 查看FatBoss有无hobby属性
In [13]: print(hasattr(FatBoss,'hobby'))
True
# 打印看看hobby属性,发现完全将FatBoy复制过来了。
In [14]: print(FatBoss.hobby)

肥仔可乐水

In [15]:

动态创建类

>>> 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()

# 你可以通过这个类创建类实例,也就是对象

但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。还记得内建函数type吗?这个古老但强大的函数能够让你知道一个对象的类型是什么,就像这样:

>>> print type(1)
>>> print type("1")
>>> print type(ObjectCreator)
>>> print type(ObjectCreator())

元类的主要目的就是为了当创建类时能够自动地改变类

通常,你会为API做这样的事情,你希望可以创建符合当前上下文的类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定metaclass。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。

幸运的是,metaclass实际上可以被任意调用,它并不需要是一个正式的类(我知道,某些名字里带有‘class’的东西并不需要是一个class,画画图理解下,这很有帮助)。所以,我们这里就先以一个简单的函数作为例子开始。

def upper_attr(future_class_name, future_class_parents, future_class_attr):
'''返回一个类对象,将属性都转为大写形式'''
# 选择所有不以'__'开头的属性
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
# 将它们转为大写形式
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 通过'type'来做类对象的创建
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr
# 这会作用到这个模块中的所有类
class Foo(object):
# 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
bar = 'bip'
print hasattr(Foo, 'bar')
# 输出: False
print hasattr(Foo, 'BAR')
# 输出:True
f = Foo()
print f.BAR
# 输出:'bip'

Python中的一切都是对象,它们要么是类的实例,要么是元类的实例,除了type。type实际上是它自己的元类,在纯Python环境中这可不是你能够做到的,这是通过在实现层面耍一些小手段做到的。其次,元类是很复杂的。对于非常简单的类,你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类:

1) Monkey patching
2) class decorators
当你需要动态修改类时

,99%的时间里你最好使用上面这两种技术。当然了,其实在99%的时间里你根本就不需要动态修改类;:hankey: :poop: :shit: