首页 > 技术文章 > Python类和对象

Teyisang 2020-08-09 14:59 原文

1.Python解释器对于class的释义

A class definition defines a class object (see section The standard
type hierarchy):

   classdef    ::= [decorators] "class" classname [inheritance] ":" suite
   inheritance ::= "(" [argument_list] ")"
   classname   ::= identifier

A class definition is an executable statement.  The inheritance list
usually gives a list of base classes (see Metaclasses for more
advanced uses), so each item in the list should evaluate to a class
object which allows subclassing.  Classes without an inheritance list
inherit, by default, from the base class "object"; hence,

   class Foo:
       pass

is equivalent to

   class Foo(object):
       pass

The class’s suite is then executed in a new execution frame (see
Naming and binding), using a newly created local namespace and the
original global namespace. (Usually, the suite contains mostly
function definitions.)  When the class’s suite finishes execution, its
execution frame is discarded but its local namespace is saved. [3] A
class object is then created using the inheritance list for the base
classes and the saved local namespace for the attribute dictionary.
The class name is bound to this class object in the original local
namespace.

The order in which attributes are defined in the class body is
preserved in the new class’s "__dict__".  Note that this is reliable
only right after the class is created and only for classes that were
defined using the definition syntax.

Class creation can be customized heavily using metaclasses.

Classes can also be decorated: just like when decorating functions,

   @f1(arg)
   @f2
   class Foo: pass

is roughly equivalent to

   class Foo: pass
   Foo = f1(arg)(f2(Foo))

The evaluation rules for the decorator expressions are the same as for
function decorators.  The result is then bound to the class name.

**Programmer’s note:** Variables defined in the class definition are
class attributes; they are shared by instances.  Instance attributes
can be set in a method with "self.name = value".  Both class and
instance attributes are accessible through the notation “"self.name"”,
and an instance attribute hides a class attribute with the same name
when accessed in this way.  Class attributes can be used as defaults
for instance attributes, but using mutable values there can lead to
unexpected results.  Descriptors can be used to create instance
variables with different implementation details.

See also:

  **PEP 3115** - Metaclasses in Python 3000
     The proposal that changed the declaration of metaclasses to the
     current syntax, and the semantics for how classes with
     metaclasses are constructed.

  **PEP 3129** - Class Decorators
     The proposal that added class decorators.  Function and method
     decorators were introduced in **PEP 318**.

Related help topics: CLASSES, SPECIALMETHODS
class 原文
类定义定义了一个类对象(参见标准部分)

类型层次结构):



classdef::= [decorator] "class" classname[继承]":" suite

继承::= "(" [argument_list] ")"

类名::=标识符



类定义是一个可执行语句。继承列表

通常给出基类的列表(更多信息请参阅元类)

所以列表中的每一项都应该被评估为一个类

对象,该对象允许子类化。没有继承列表的类

默认情况下继承基类“object”;因此,



类Foo:

通过



相当于



类Foo(对象):

通过



然后类的套件在一个新的执行框架中执行(参见

),使用新创建的本地名称空间和

原来的全局名称空间。(通常,套件包含了大部分

函数定义)。当类的套件完成执行时,它的

执行框架被丢弃,但其本地名称空间被保存。[3]一个

然后使用基类的继承列表创建类对象

类和保存的属性字典的本地名称空间。

类名在原始本地绑定到这个类对象

名称空间。



类主体中定义属性的顺序为

保留在新类别的“剩余书写__”中。注意,这是可靠的

仅在类创建之后,且仅适用于已创建的类

使用定义语法定义。



可以使用元类大量定制类的创建。



类也可以被装饰:就像装饰函数一样,



@f1 (arg)

@f2

类Foo:通过



大致相当于



类Foo:通过

Foo = f1 (arg) (f2 (Foo))



装饰器表达式的计算规则与for相同

函数修饰符。然后将结果绑定到类名。



**程序员注意:**在类定义中定义的变量是

类属性;它们由实例共享。实例属性

可以在方法中设置"self。name = value"。类和

实例属性可以通过“self。name”符号访问,

实例属性隐藏具有相同名称的类属性

以这种方式访问时。类属性可以作为默认值使用

但是使用可变值可能导致

意想不到的结果。描述符可用于创建实例

具有不同实现细节的变量。



参见:



**PEP 3115** - Python 3000中的元类

将元类的声明更改为

当前的语法,和如何类的语义

元类的构造。



**PEP 3129** -类装饰器

添加类装饰器的建议。函数和方法

decorator是在**PEP 318**中引入的。



相关帮助主题:类、特殊方法
class 译文 凑合看

 从译文里 大致能得到类的几个点(根据翻译来的):

1.python有元类,我们创建的类默认继承自object类

2.类在创建的时候,起作用的有两个东西,一个是类的本地名称空间,一个是父类的继承列表。创建完成后,类名会被绑定到父类的本地名称空间?

3.类创建之后,类的属性被保存在这个新类的__dict__里头

4.类也可以像函数一样 运用装饰器,使用方法和函数装饰器是一样的。

5.在类中定义的变量是类的属性,类的属性可以被类的实例使用。实例属性可以通过 "self.name = value"这种方式去定义。实例属性和类属性都可以通过"self.name"去调用,用这种方式调用的时候,如果实例属性和类属性名字相同,实例属性会覆盖类属性。类属性相当于实例属性的默认值。但是要注意,不要将类属性定义成可变类型。

 2.面向对象总结

面向对象核心是 减少重复代码,将代码可以拆分成块。就像拼图一样,打散是为了最后的统一。

面向对象里头概念比较多

类 《-----》对象: 对象可以继承类的属性,对象可以有自己的属性,对象的绑定方法。类的属性 是对象的共有属性。类的方法通过@classmethod实现,绑定到类的方法只能访问类属性,不能访问对象属性。

类:

- 类属性

- 类方法, 加上@classmethod装饰器,会自动将类做为参数传递给类方法。

对象:

- 绑定方法

- 对象属性 通过__init__()传参,初始化对象

- 属性:通过@property实现,将方法伪装成变量

静态方法:通过@staticmethod实现,不需要用到传递 对象self进来。

父类 《----》子类: 子类可以继承父类的一切。子类可以改写父类的一切,除了父类的私有属性和私有方法。所以如果说父类是有私有方法或者属性的话,建议的话不要设置成父类。父类应该很简单的,就像虚无衍生万物的概念。

对象之间的交互、组合:对象之间有四种关系,依存关系,关联关系,组合关系,聚合关系。这些关系通过代码如何实现

类的装饰器:@porperty把方法装饰成属性 @classmethod 定义类方法  @staticmethod 定义静态方法abc.ABCMeta @abc.abstractmethod 定义抽象方法,无需实现功能

反射:可把字符串的引号去掉。实现用户和程序的交互,反射可以用于一切对象,实例,类,模块等等

内置方法:

 

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)

__setitem__,__getitem,__delitem__
getitem,setitem,delitem

 

# 1 初始化方法:__init__
class A(object):
    def __init__(self):
        print("初始化执行方法")
A()

# 2 构造方法:__new__
class B(object):

    def __new__(cls,*args,**kwargs):
        print("我是用来开辟一块空间的")
        obj=super().__new__(cls)
        return obj

    def __init__(self):
        print("self就是__new__开辟的空间地址")

B()

# 应用:比如单例模式

# 3 __str__

class C(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):  # 必须返回字符串类型
        return self.name

c1=C("c1",20)
c2=C("c2",23)
print(c1)
print(c2)

# 4 __call__
class D(object):

    def __call__(self, *args, **kwargs):
        print("call 被调用...")

print(callable(D))
d1=D()
print(callable(d1))
d1()

# 5 析构方法 __del__

class F(object):
    def __del__(self):
        print("删除对象时被调用!")

f=F()
# del f # 思考:注释掉为什么也会调用__del__
# import time
# time.sleep(100)

# 应用

class Filehandler(object):
    file="a.text"
    def __init__(self):
        self.f=open(self.file)

    def __del__(self):
        self.f.close()



# 6 __getitem__


class G(object):
    def __init__(self):
        pass

    def __getitem__(self,item):
        print("__getitem__被调用")

    def __setitem__(self, key, value):
        print("__setitem__被调用")

    def __delitem__(self, key):
        print("__delitem__被调用")

g=G()
g["name"]="alex"
print(g["name"])
del g["name"]

# 7 __getattr__

class H(object):
    def __init__(self):
        pass

    def __getattr__(self, item):
        print("__getattr__被调用")

    def __setattr__(self, key, value):
        print("__setattr__被调用")

    def __delattr__(self, item):
        print("__delattr__被调用")

h=H()
h.name="alex"
print(h.name)
del h.name


# 8 __eq__

class I(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __eq__(self, other):

        if self.name==other.name and self.age==other.age:
            return True
        else:
            return False

i1=I("alex",30)
i2=I("alex",30)
print(i1==i2)


# __len__

class G(object):
    def __len__(self):
        return 100

g=G()
print(len(g))
类的魔法方法
"""
l=[123,456,789]
info={"name":"alex","age":1000}
s="hello"
print(len(l))
print(len(info))
print(len(s))
"""


##################### 归一化设计 #####################

# 支付宝 微信 银行卡 nfc支付

class AliPay(object):
    def __init__(self,name,money):
        self.money=money
        self.name=name
    def pay(self):
        # 支付宝提供了一个网络上的联系渠道
        print('%s通过支付宝消费了%s元'%(self.name,self.money))

class WeChatPay(object):
    def __init__(self,name,money):
        self.money=money
        self.name=name
    def pay(self):
        # 微信提供了一个网络上的联系渠道
        print('%s通过微信消费了%s元'%(self.name,self.money))

def pay_func(pay_obj):
    pay_obj.pay()

alipay=AliPay("alex",100)
wechatpay=WeChatPay("yuan",200)

pay_func(alipay)
pay_func(wechatpay)


##################### 规范化方法 #####################
'''

规范化方法
支付宝 微信 银行卡 nfc支付
同事协作之间的代码规范问题
规定:Payment 就是一个规范类,这个类存在的意义不在于实现实际的功能,而是为了约束所有的子类必须实现pay的方法
Payment : 抽象类
    pay = Payment() # 抽象类: 不能实例化
    抽象类主要就是作为基类/父类,来约束子类中必须实现的某些方法
    抽象类的特点:
        必须在类定义的时候指定metaclass = ABCMeta
        必须在要约束的方法上方加上@abstractmethod方法
'''



from abc import ABCMeta,abstractmethod #(抽象方法)

class Payment(metaclass=ABCMeta):   # metaclass 元类  metaclass = ABCMeta表示Payment类是一个规范类
    def __init__(self,name,money):
        self.money=money
        self.name=name

    @abstractmethod      # @abstractmethod表示下面一行中的pay方法是一个必须在子类中实现的方法
    def pay(self,*args,**kwargs):
        pass

    @abstractmethod
    def back(self):
        pass

class AliPay(Payment):

    def pay(self):
        # 支付宝提供了一个网络上的联系渠道
        print('%s通过支付宝消费了%s元'%(self.name,self.money))

class WeChatPay(Payment):

    def pay(self):
        # 微信提供了一个网络上的联系渠道
        print('%s通过微信消费了%s元'%(self.name,self.money))

def pay_func(pay_obj):
    pay_obj.pay()

alipay=AliPay("alex",100)
wechatpay=WeChatPay("yuan",200)

pay_func(alipay)
pay_func(wechatpay)

#当子类和父类都存在相同的pay()方法时,我们说,子类的pay()覆盖了父类的pay(),
# 在代码运行的时候,总是会调用子类的pay()。这样,我们就获得了继承的另一个好处:多态。


'''
你会发现,新增一个Payment的子类,不必对pay()做任何修改,实际上,任何依赖Payment作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

多态的好处就是,当我们需要传入AliPay、WeChatPay、ApplePay……时,我们只需要接收Payment类型就可以了,因为AliPay、WeChatPay、ApplePay……都
是Payment类型,然后,按照Payment类型进行操作即可。由于Payment类型有pay()方法,因此,传入的任意类型,只要是Payment类或者子类,就会自动调用实
际类型的pay()方法,这就是多态的意思:

对于一个变量,我们只需要知道它是Payment类型,无需确切地知道它的子类型,就可以放心地调用pay()方法,而具体调用的pay()方法是作用在AliPay、WeChatPay、
ApplePay哪个类对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Payment的子类时,只要确保pay()方
法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

      对扩展开放:允许新增Payment子类;
      对修改封闭:不需要修改依赖Payment类型的pay()等函数。
'''

class ApplePay(Payment):
    def pay(self):
       print('%s通过苹果支付消费了%s元'%(self.name,self.money))

applepay=ApplePay("egon",800)



##################### 鸭子类型 #####################

"""
对于静态语言(例如Java)来说,如果需要传入Payment类型,则传入的对象必须是Payment类型或者它的子类,否则,将无法调用pay()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个pay()方法就可以了:
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
"""

class CardPay(object):
    def __init__(self,name,money):
        self.money=money
        self.name=name
    def pay(self):
       print('%s通过银联卡支付消费了%s元'%(self.name,self.money))

def pay_func(pay_obj):
    pay_obj.pay()

cp=CardPay("alvin",1000)
pay_func(cp)

归一化设计
抽象类
class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它


    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)

#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)

#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)

#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

__setattr__,__delattr__,__getattr__
__setattr__,__getattr__,__delattr__

__getattr__只有在使用点调用属性且属性不存在的时候才会触发
__setattr__添加/修改属性会触发它的执行
__delattr__删除属性的时候会触发


class List:
    def __init__(self,x):
        self.seq=list(x)

    def append(self,value):
        if not isinstance(value,str):
            raise TypeError('must be str')
        self.seq.append(value)
    @property
    def mid(self):
        index=len(self.seq)//2
        return self.seq[index]
    def __getattr__(self, item):
        return getattr(self.seq,item)

    def __str__(self):
        return str(self.seq)

l=List([1,2,3])

l.append('1')

print(l.mid)
l.insert(0,123123123123123123123123123)
# print(l.seq)
print(l)
class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果
执行我啦
------->

######################
class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
# del f1
print('------->')

#输出结果
------->
执行我啦

__del__
__del__

 super()父类,这里的父类是mro中的父类。

class A:
    def func(self):
        super().func() #B里的func,此处的super是找mro中的下一个
        print('AAA')
class B:
    def func(self):
        print('BBB')

class C(A,B):
    pass
c=C()
c.func()

--super--可以访问MRO列表中的下一个类中的内容,找父类。
不管super()写在那,在那执行,一定先找到MRO列表,根据MRO列表的顺序往下找,否则一切都是错的。
   

 

import sys
def s1():
    print ('s1')

def s2():
    print ('s2')

print(sys.modules)
this_module = sys.modules[__name__]
a = getattr(this_module, 's1')
a()
print(a,type(a))
模块也是对象

 

class A:
    def __init__(self):
        pass

    def func1(self):
        print("func1",self)

    @classmethod
    def func2(self):
        print("func2",self)

    @staticmethod
    def func3(self):
        print("func3", self)

a=A()

a.func1()  #func1 <__main__.A object at 0x00000000041A52E8>
A.func1(a) #func1 <__main__.A object at 0x00000000041A52E8>   #类调用绑定到对象的方法需要传第一个参数

A.func2()  #func2 <class '__main__.A'>
a.func2()  #func2 <class '__main__.A'>  #对象调用绑定到类的方法不需要传第一个参数

a.func3(a) #func3 <__main__.A object at 0x00000000041A52E8>
A.func3(a) #func3 <__main__.A object at 0x00000000041A52E8>

a.func3(A) #func3 <class '__main__.A'>
A.func3(A) #func3 <class '__main__.A'>
绑定和非绑定方法的调用方式

 

推荐阅读