Python中的函数和方法

在Python中,对函数和方法都有明确的规定:

函数function---A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body.

方法method---A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument(which is usually called self).

Python中类的方法

类中的方法,可以分为:实例方法,类方法,静态方法。方法和字段一样,也是属于类的属性。

实例方法

实例方法就是类的实例能够使用的方法。

>>> class Foo:
    def __init__(self, name):
        self.name = name
    def hi(self):
        print self.name


>>> foo01 = Foo('letian')
>>> foo01.hi()
letian
>>> print type(Foo)
<type 'classobj'>
>>> print type(foo01)
<type 'instance'>
>>> print id(foo01)
47516040
>>> print id(Foo)
51151112
>>>

构造器__init__就是一个实例方法,特点是:

1. 方法的第一个参数必须是self,当然这是约定俗成的写法,可以将self换成abc之类的,但是为了别的程序员能看得懂,还是统一用self吧。这里的self代表实例本身,也就是说如果我实例化时使用的是:a = Test(),那么self就代表a这个实例。我们可以在很多构造器中看到类似self.scolia = 'good'的写法,其实这个写法和在类外面a.scolia = 'good'效果是一样的,都是为了添加属性,只不过__init__方法是实例化时自动调用的函数,所以适合进行初始属性的创建。

2. 实例方法在调用的时候,self是自动传递的,所以不需要我们再处理;

3. 实例方法一般要有实例才能调用,当然也有特殊的调用方法。

class Test(object):

    def __init__(self, a, b):   # 构造器在实例创建时进行属性的初始化
        self.a = int(a)
        self.b = int(b)

    def abc(self, c):  # 实例方法
        print self.a + self.b + int(c)  # 因为self是自动传递的,所以我们可以在实例方法中调用实例的属性

a = Test(123, 321)  # 我们只要为 a 和 b 传参就行了
a.abc(666)  # 同样的,只要为 c 传参

>>>1110

这里,将引入一个绑定(binding)的概念,其主要和方法的调用有关系。

这里,将引入一个绑定(binding)的概念,其主要和方法的调用有关系。
首先,我们知道方法是类的属性,而不是实例的属性。其次,方法只有在其所属的类拥有实例时,才能被调用。当一个类存在实例后,方法才被认为是绑定到这个实例。没有实例的时候,方法是未绑定的。最后,任何一个方法定义的第一个参数都是变量self,它表示调用此方法的实例对象。
很明显这里的绑定针对的是实例方法,因为如果没有实例的话,self就无法传递,这将导致参数的不足,当然就无法调用了。

但是,可以自己传递self来调用未绑定的方法。调用未绑定的方法通常是在我们继承了一个父类后,我们覆盖了父类中的某个方法,但是为了实现代码重用,我们又想在子类中调用父类的方法。单纯的复制父类中的代码明显不是一个好选择,除了浪费系统资源之外,还有可能在复制的时候出错,而且以后修改父类的代码之后,还要修改相应子类中的代码,实在太低效,这个时候就是调用未绑定方法的场景。

class abc(object):
    def __init__(self, a):
        self.a = -int(a)

class Test(abc):
    def __init__(self, a, b):
        abc.__init__(self, a)   # 调用父类的构造器,并手动传递 self
        self.b = b

    def fangfa(self):
        print self.a + self.b   # 属性 a 由父类的构造器创建,b 由子类构造器创建

a = Test(123, 321)  # 我们只创建了子类的实例,而没有创建父类的实例
a.fangfa()

>>>198

本来我们没有创建父类的实例,是无法调用父类的实例方法的,但是我们手动传递了实例方法所需要的self参数,就可以实现调用了。
这里的顺序是,我们创建了Test的实例a,其self是自动传递的,故Test的构造方法__init__(self, a, b)中self就代表实例a,而我们又调用了父类的abc.__init__(self, a)这里的self就是子类的实例a,参数a就是我们传的123,而父类中self.a = -int(a),最后我们可以在子类的方法中调用self.a这个属性。

类方法

类方法和实例方法类似,不过其第一个参数一般是cls(约定俗成)而不是self。但是,如果我们直接将self换成cls来创建类方法是不对的,因为实例方法的首个参数也是任意的,只是统一使用self。Python的解释器并没有说看见第一个参数是cls就知道这个是类方法,它还是将其当做是实例方法来对待,所有我们需要通过内建函数:classmethod()来创建类方法。

class Test(object):

    def abc(cls):
        print cls.__name__  # 打印类名

    abc = classmethod(abc)  # 通过普通的函数传参的方式创建类方法

a = Test()  
Test.abc()  # 类能调用
a.abc()     # 实例也能调用

>>>Test
>>>Test

当然,也可以使用装饰器的方法来创建类方法:

class Test(object):
    @classmethod
    def abc(cls):
        print cls.__name__  # 打印类名

结果也是一样的,具体选择看个人喜好。注意,因为使用了classmethod()函数,其第一个参数cls是固定传递的,而且是代表当前的类。并没有实例方法中非绑定方法的调用形式。

静态方法

静态方法其实就是类中的一个普通函数,就位于类定义的命名空间中,它不会对任何实例进行操作,类和实例都可以调用静态方法。它并没有默认传递的参数,在创建静态方法的时候,需要用到内置函数:staticmethod()。使用静态方法的好处是,不需要定义实例即可使用这个方法。另外,多个实例共享此静态方法。

class Test(object):

    def abc():
        print 'abc'

    abc = staticmethod(abc)

    @staticmethod
    def xyz(a, b):
        print a + b

Test.abc()  # 类调用
Test.xyz(1, 2)  # 类调用
a = Test()
a.abc()     # 实例调用
a.xyz(3, 4)  # 实例调用

>>>abc
>>>3
>>>abc
>>>7

注意,虽然静态方法没有默认参数,但并不代表不能有参数。

用方法冒充字段

有时候,一个方法在经过一系列处理以后,返回的是一个数据,例如:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa(self):
        c = self.a + self.b
        return c    # 返回处理的结果数据


a = Test(1, 2)
b = a.fangfa()    # 调用方法,得到返回值
print b

>>>3

但是,懒惰的程序员们想,我想要的只是和字段类似的数据,却要调用一个方法,有时容易搞错,能不能用字段的形式获取数据呢?这样更加符合直觉。
可以,只有使用property()函数就可以了。同样的,这里也有两种创建方式,这里只演示装饰器的方法。

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c


a = Test(1, 2)
b = a.fangfa    # 不用带括号了
print b

这样是实现了方法伪装成字段了,其实就是懒惰的程序员们不愿意多写一个括号,当然还有一些其他好处。
另外,函数要有返回值,不然就默认为None了。
如果在经典类中,我们就只能做到这样了。
但是,使用新式类的话,就能有更多的功能:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c

    @fangfa.setter
    def fangfa(self, value):
        self.a = value

    @fangfa.deleter
    def fangfa(self):
        print '属性已删除'

a = Test(1, 2)
b = a.fangfa   # 获得方法的返回值
print b
a.fangfa = 100    # 执行 fangfa.setter 修饰的方法,并让value = 100
print a.a
del a.fangfa    # 执行 fangfa.deleter 修饰的方法

>>>3
>>>100
属性已删除

注意后面两个装饰的名字。
另外,方法必须要先经过property()函数的装饰后,才有后面两个装饰器的用法。

如果使用非装饰器的形式的话,

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa_get(self):
        c = self.a + self.b
        return c

    def fangfa_set(self, value):
        self.a = value

    def fangfa_del(self):
        print '属性已删除'

    fangfa = property(fangfa_get, fangfa_set, fangfa_del)
property(fget=None, fset=None, fdel=None, doc=None

最后的doc是说明文档,看个人需要添加,可以通过Test.fanda.__doc__进行访问。

Super

super用来执行父类中的方法。在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可以通过使用super来实现。

class Animal(object):
    def __init__(self, name):
        self.name = name
    def greet(self):
        print 'Hello, I am %s.' % self.name

class Dog(Animal):
    def greet(self):
        super(Dog, self).greet()   # Python3 可使用 super().greet()
        print 'WangWang...'

>>> dog = Dog('dog')
>>> dog.greet()
Hello, I am dog.
WangWang..
super(type[, object-or-type])
Return the superclass of type. If the second argument is omitted the super object returned is unbound. If the second argument is an object, isintance(obj,type) must be true. If the second argument is a type, issubclass(type2, type) must be true, super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
class C(B):
  def meth(self, arg):
    super(C, self).meth(arg)
class Foo(object):
    def hi(self):
        print 'hi,Foo'
class Foo2(Foo):
    def hi(self):
        super(Foo2, self).hi()
if __name__ == '__main__':
    foo2 = Foo2()
    foo2.hi()

>>>hi, Foo

注意,Foo类必须继承某个类(并且这个继承链开始于object类),否则会报错。如果改成下面的形式:

class Foo:
    def hi(self):
        print 'hi,Foo'
class Foo2(Foo):
    def hi(self):
        super(Foo2, self).hi()
if __name__ == '__main__':
    foo2 = Foo2()
    foo2.hi()

运行结果如下,
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    foo2.hi()
  File "<pyshell#15>", line 3, in hi
    super(Foo2, self).hi()
TypeError: must be type, not classobj

super 的一个最常见用法可以说是在子类中调用父类的初始化方法了,比如:

class Base(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

class A(Base):
    def __init__(self, a, b, c):
        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)
        self.c = c

MRO列表

事实上,对于你定义的每一个类,Python都会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的MRO列表:

>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

一个类的MRO列表就是合并所有父类的MRO列表,并遵循以下三条原则:

1.子类永远在父类前面;

2.如果有多个父类,会根据它们在列表中的顺序被检查;

3.如果对下一个类存在两个合法的选择,选择第一个父类;

Super原理

super的工作原理如下:

def super(cls, inst):
  mro = inst.__class__.mro()
  return mro[mro.index(cls) + 1]

其中,cls代表类,inst代表实例,上面的代码做了两件事:

1. 获取inst的MRO列表;

2. 查找cls在当前MRO列表中的index,并返回它的下一个类,即mro[index+1];

当你使用super(cls, inst)时,Python会在inst的MRO列表上搜索cls的下一个类。

现在,让我们回到前面的例子。

首先,看类C的__init__方法:

super(C, self).__init__()

这里的self是当前C的实例,self.__class__.mro()结果是:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

可以看到,C的下一个类是A,于是,跳到了A的__init__,这是会打印出enter A,并执行下面一行代码:

super(A, self).__init__()

注意,这里的self也是当前C的实例,MRO列表跟上面是一样的,搜索A在MRO列表中的下一个类,发现是B,于是,跳到了B的__init__,这时会打印出enter B,而不是enter Base。

整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。
事实上,super和父类没有实质性的关联,super(cls, inst)获得的是cls在inst的MRO列表中的下一个类。

Python中repr和str的区别

repr语法: repr[object]

返回一个可以表示对象的可打印的字符串。首先,尝试生成一个这样的字符串,然后将其传递给eval(),可以重新生成同样的对象。否则,生成用尖括号包住的字符串,包含类型名称和额外的信息(比如地址)。一个类(class)可以通过__repr__()成员来控制repr()函数作用在其实例上时的行为。但是repr所返回的对象更适合于解释器去阅读,可以理解为亲近python。

Return a string containing a printable representation of an object. This is the save value yielded by conversions(reverse quotes). It is sometimes useful to be able to access this operation as an ordinary function. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a repr() method.

str语法: str[object]

返回一个可以表示对象的友好的可打印的字符串。对于字符串则返回本身,如果没有参数,则返回空字符串。对类,可以通过__str__()成员控制其行为,该成员不存在,则使用其__repr__()成员。str返回的对象更合人类阅读,str致力于返回一个可读性比较好的对象,返回的结构通常不会通过eval()去处理。与repr的区别:不总是尝试生成一个传给eval的字符串,其目标是可打印字符串。

Return a string containing a nicely printable representation of an object. For strings, this returns the string itself. The difference with repr(object) is that str(object) does not always attempt to return a string that is acceptable to eval(); its goal is to return a printable string. If no argument is given, returns the empty string.
The str() function is meant to return representations of values which are fairly human-readable.
While repr() is meant to generate representations which can be read by the interpreter.
For objects which don't have a particular representation for human consumption, str() will return the same value as repr().
Many values, such as numbers or structures like lists and dictionaries, have the same representation using either function. Strings and float point numbers, in particular, have two distinct representations.

看下面的例子就明白了

class Test(object):
  def __init__(self, value='hello, world!'):
    self.data = value

>>>t = Test()
>>>t
<__main__.Test at 0x7fa91c307190>
>>>print t
<__main__.Test object at 0x7fa91c307190>

看到了么?上面打印类对象并不是很友好,显示的是对象的内存地址。
下面我们重构下该类的__repr__以及__str__,看看它们俩有啥区别。

重构__repr__

class TestRepr(Test):
  def __repr__(self):
    return 'TestRepr(%s)' %self.data

>>>tr = TestRepr()
>>>tr
TestRepr(hello,world!)
>>>print tr
TestRepr(hello,world!)

重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按照我们__repr__方法中定义的格式进行显示了。

重构__str__

class TestStr(Test):
  def __str__(self):
    return '[Value: %s]' %self.data
>>>ts = TestStr()
>>>ts
<__main__.TestStr at 0x7fa91c314e50>
>>>print ts
[Value: hello, world!]

你会发现,直接输出对象ts时并没有按照我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了。
__repr____str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员。

1. 打印操作会首先尝试__str__str(print运行的内部等价形式)内置函数,它通常应该返回一个友好的显示。

2. __repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,如果没有使用__str__,会使用printstr,它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。
当我们想所有环境下都统一显示的话,可以重构__repr__方法;当我们想在不同环境下支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__以得到更友好的用户显示。

eval函数

将字符串str当成有效的表达式来求值并返回计算结果。

语法: eval(source[, globals[,locals]]) - > value
参数:
source: 一个Python表达式或函数compile()返回的代码对象;
globals: 可选。必须是dictionary。
locals: 可选。任意map对象。

可以把list,tuple,dict和string相互转化。
#################################################
#字符串转换成列表
>>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]"
>>>type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]
>>> type(b)
<type 'list'>
#################################################
#字符串转换成字典
>>> a = "{1: 'a', 2: 'b'}"
>>> type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
{1: 'a', 2: 'b'}
>>> type(b)
<type 'dict'>
#################################################
#字符串转换成元组
>>> a = "([1,2], [3,4], [5,6], [7,8], (9,0))"
>>> type(a)
<type 'str'>
>>> b = eval(a)
>>> print b
([1, 2], [3, 4], [5, 6], [7, 8], (9, 0))
>>> type(b)
<type 'tuple'>

但是强大的函数有代价,安全性是其最大的缺点。
想一想这种使用环境:需要用户输入一个表达式,并求值。
如果用户恶意输入,例如:

__import__('os').system('dir')

那么eval()之后,你会发现,当前目录文件都会展现在用户前面。
那么继续输入:

open('文件名').read()

代码都给人看了,获取完毕,一条删除命令,文件消失。
eval函数在Python中做数据类型转换还是很有用的。它的作用就是把数据还原成它本身或者是能够转化成的数据类型。
那么eval和ast.literal_val()的区别是什么呢?
eval在做计算前并不知道需要转化的内容是不是合法的(安全的)python数据类型。只是在调用函数的时候去计算,如果被计算的内容不是合法的python类型就会抛出异常。
ast.literal_eval则会判断需要计算的内容,计算后是不是合法的python类型,如果是,则进行运算,否则就不进行运算。

datamap = eval(raw_input('Provide some data here:')) means that you actually evaluate the code before you deem it to be unsafe or not.It evaluates the code as soon as the function is called. See also the dangers of eval.
ast.literal_eval raises an exception if the input isn't valid python datatype, so the code won't be executed if it's not.
Use ast.literal_eval whenever you need eval. If you have Python expressions as an input that you want to evaluate, you should not(have them).

unbound method和bound method

>>> class C(object):
    def foo(self):
        pass


>>> C.foo
<unbound method C.foo>
>>> C().foo
<bound method C.foo of <__main__.C object at 0x0000000002A1C9E8>>
>>> class A():
    def foo(self):
        pass


>>> A.foo
<unbound method A.foo>
>>> A().foo
<bound method A.foo of <__main__.A instance at 0x00000000029C5B88>>
>>>

为什么C.foo是一个unbound method,C().foo是一个bound method?Python为什么这样设计?
如果明白Python中描述器(descriptor)是怎么实现的,方法(method)是很容易理解的。
上例代码中可以看到,如果你用类C去访问foo方法,会得到unbound方法,然而在class的内部存储中它是个function,为什么?原因就是C的类(注意是类的类)实现了一个__getattribute__来解析描述器。听起来复杂,但并非如此。上例中的C.foo等价于:

>>> C.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'C' objects>, '__module__': '__main__', 'foo': <function foo at 0x0000000002A1E7B8>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None})
>>> C.__dict__['foo']
<function foo at 0x0000000002A1E7B8>
>>> C.__dict__['foo'].__dict
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>>

这是因为方法foo有个__get__方法,也就是说,方法是个描述器。如果你用实例来访问的话也是一模一样的:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x0000000002AF00B8>>
>>>

只是那个None换成了这个实例。现在我们来讨论,为什么Python要这么设计?
其实,所谓bound method,就是方法对象的第一个函数参数绑定为这个类的实例(所谓bind)。这也是那个self的由来。
当你不想让类把一个函数作为一个方法,可以使用装饰器staticmethod。

>>> class C(object):
...     @staticmethod
...     def foo():
...         pass
...
>>> C.foo
<function foo at 0xb76d056c>
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0xb76d056c>

staticmethod装饰器会让foo的__get__返回一个函数,而不是一个方法。

function,bound method和unbound method的区别是什么?

一个函数(function)是由def语句或者lambda创建的。

当一个函数(function)定义在了class语句的块中(或者由type来创建的),它会转成一个unbound method,当我们通过一个类的实例来访问这个函数的时候,它就转成了bound method,bound method会自动把这个实例作为函数的第一个参数。

所以,bound method就是绑定了一个实例的方法,否则叫做unbound method。它们都是方法(method),是出现在class中的函数。

类变量和实例变量

类变量定义在类的定义中,实例变量则是以self开头。实例也能够访问类变量。

只要声明在类的语句块中,且没有'self'前缀的变量都是类变量,且类变量是被所有对象共享的。

如果在类的方法的语句块中声明,那么就是局部变量了,比如下面这个例子:

>>> class Person:
    cvar = 1
    def sayHi(self):
        fvar = 2
>>> Person.cvar
1
>>> Person.fvar
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    Person.fvar
AttributeError: class Person has no attribute 'fvar'

那么这个cvar就是属于Python类的变量,而那个fvar就是方法sayHi()中的局部变量。
在类的语句块及其中方法的语句块中声明的,以'self'开头的变量都是实例变量,由对象独有!

>>> class Person:
    def haveName(self):
        self.name = 'Michael'
    def sayName(self):
        print self.name
>>> p=Person()
>>> p.sayName()
Traceback (most recent call last):
  File "<pyshell#59>", line 1, in <module>
    p.sayName()
  File "<pyshell#57>", line 5, in sayName
    print self.name
AttributeError: Person instance has no attribute 'name'
>>> p.haveName()
>>> p.sayName()
Michael
>>>

这里在haveName()方法中声明了一个实例变量,然后在sayName()方法中调用。

不过,建议将实例变量声明在__init__()方法中,因为对象一被创建的时候即会调用这个方法,否则的话,比如上面那个例子,如果我先调用sayName()的话,那么就会出错,说对象实例还没有name这个属性!

类方法中self.xxx和xxx的区别

Python类方法中的内部变量,命名加'self.'变成self.xxx和xxx的区别。

>>> class nc():
          def init(self):
              self.name = 'tester' #name变量加self
>>> class mc():
          def init(self):
              name = 'tester' #name变量不加self
>>> NC = nc()
>>> NC.name
'tester'
>>> nc.name
  Traceback (most recent call last):
  File "<pyshell#86>", line 1, in <module>
    nc.name
  AttributeError: class nc has no attribute 'name'
>>> MC = mc()
>>> MC.name
  Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    MC.name
  AttributeError: mc instance has no attribute 'name'
>>> mc.name
  Traceback (most recent call last):
  File "<pyshell#89>", line 1, in <module>
    mc.name
  AttributeError: class mc has no attribute 'name'
>>>

python类方法中的变量不加self,也就是xxx,那么这个是方法的局部变量。

其它

Python的静态方法和类方法都可以被类或实例访问,但是还是有区别的:

1. 静态方法无需传入self参数,类方法需要传入代表本类的cls参数;

2. 静态方法是无法访问实例变量的,而类方法也同样无法访问实例变量,但可以访问类变量;

3. 静态方法有点像函数工具库的作用。

在Python2.4及之后,用装饰器(decorators)实现,

class MyClass:
    val1 = 'Value 1'
    def __init__(self):
        self.val2 = 'Value 2'

    @staticmethod
    def staticmd():
        print '静态方法,无法访问val1和val2'

    @classmethod
    def classmd(cls):
        print '类方法,类:' + str(cls) + ',val1:' + cls.val1 + ',无法访问val2的值'

>>> mc = MyClass()  # 实例化
>>> mc.staticmd()  # 实例调用静态方法,无法访问实例变量val1和val2
>>> 
静态方法,无法访问val1和val2

>>> mc.classmd()  # 实例调用类方法,注意,这里访问的是类MyClass的变量val1的值,不是实例化后mc的实例变量val1,这里容易混淆,往下看就会明白。val2一直是实例变量,所以无法访问
>>> 
类方法,类:__main__.MyClass,val1:Value 1,无法访问val2的值

>>> MyClass.staticmd()  # 类直接调用静态方法,结果同上面的实例调用,无论是类变量还是实例变量都无法访问
>>> 
静态方法,无法访问val1和val2

>>> MyClass.classmd()  # 类直接调用类方法,结果同上面的实例调用
>>> 
类方法,类:__main__.MyClass,val1:Value 1,无法访问val2的值

>>> mc.val1 = 'Value changed'  # 改变实例变量val1的值

>>> mc.classmd()  # 实例调用类方法,注意到cls.val1的值没变,所以,这时的cls.val1是类变量val1,而非实例变量val1
>>> 
类方法,类:__main__.MyClass,val1:Value 1,无法访问val2的值

>>> MyClass.classmd()  # 类直接调用类方法,结果同上面的实例调用
>>> 
类方法,类:__main__.MyClass,val1:Value 1,无法访问val2的值

>>> MyClass.val1 = 'Class Value changed'  # 改变类变量val1的值

>>> mc.classmd()  # 实例调用类方法,注意到cls.val1的值变了,所以,进一步证明了这时的cls.val1是类变量val1,而非实例变量val1
>>> 
类方法,类:__main__.MyClass,val1:Class Value changed,无法访问val2的值

>>> MyClass.classmd()  # 类直接调用类方法,结果同上面的实例调用
>>> 
类方法,类:__main__.MyClass,val1:Class Value changed,无法访问val2的值

1.  静态方法: 无法访问类属性、实例属性,相当于一个相对独立的方法,跟类其实么什么关系,换个角度来讲,其实就是放在一个类的作用域里的函数而已。

2. 类方法: 可以访问类属性,无法访问实例属性。

总结

1. 实例方法需要至少一个默认参数self。

2. 类方法需要至少一个默认参数cls。

3. 静态方法不需要默认参数。

另外:方法也是属性,所以在类的内部,我们可以通过self.abc(),cls.abc()的方式来调用类中的其它方法,当然要注意传参的问题。

posted on 2020-09-01 17:16  不要挡着我晒太阳  阅读(488)  评论(0编辑  收藏  举报

导航