python编程 基础入门学习笔记七——面向对象编程

本节笔记

1.类的成员:字段、方法和属性

2.类成员的修饰符

3.类的特殊成员

 

1.类的成员:字段、方法和属性                                                                                                                                   

类的成员主要分为三大类:

  字段:静态字段、普通字段

  方法:类方法、静态方法、普通方法

  属性:普通属性

1)字段

#-*- coding:utf-8 -*-

class Student:
    #静态字段
    school_address="上海"
    def __init__(self,name):
        #普通字段
        self.name=name


#直接访问静态字段
print(Student.school_address)

#直接访问普通字段
obj=Student("Jack")
print(obj.name)
字段的定义和访问

由上述代码可以看出:静态字段通过类来访问,普通字段通过对象来访问。

本质上两者的区别是:它们在内存中保存的位置不同,

    静态字段属于类,在内存中只保存一份

    普通字段属于对象,在每个对象中都要保存一份

问题:什么时候使用静态字段呢?

通过类创建对象的时候,如果每个对象都具有相同的字段,那么就使用静态字段。
答案

2)方法

#-*-coding=utf-8-*-
class ClassName:
    def __init__(self,name):
        self.name=name

    def func(self):
        '''定义普通方法,至少有一个self参数'''
        print("普通方法",self)

    @classmethod
    def class_func(cls):
        '''定义类方法,至少有一个cls参数'''
        print("类方法",cls)

    @staticmethod
    def static_func():
        '''定义静态方法,无默认参数'''
        print("静态方法")

#调用普通方法
f=ClassName("Rose")
f.func()
#调用类方法
ClassName.class_func()

#调用静态方法
ClassName.static_func()
方法的定义和调用

由上述代码可以看出:普通方法、类方法、静态方法在内存中都属于类,即在内存中均保存一份。

三者的不同在于它们的调用方式:

    普通方法,第一个参数需要是self,它表示一个具体的实例本身;执行时,自动将调用该方法的对象赋值给self
    类方法(classmethod),它的第一个参数不是self,是cls,它表示这个类本身执行时,自动将调用该方法的赋值给cls

    静态方法(staticmethod),无视这个self,而将这个方法当成一个普通的函数使用。

3)属性

#-*- coding=utf-8 -*-
class ClassName:
    def __init__(self):
        pass
    def func(self):
        print("普通方法",self)
    
    #定义属性
    @property
    def property_func(self):
        print("属性")

#调用属性
p=ClassName()
p.property_func
属性的定义和调用

由上述代码可以看出:python中的属性就是普通方法的变种,如果python中没有属性,方法完全可以替代其功能

需要注意的是:

  定义时,在普通函数的基础上添加@property装饰器,且这个普通函数仅有一个self参数。

  调用时,不要加括号,即 p.property_func。

  属性存在的意义是——访问属性时,可以制造出和访问字段完全相同的假象。

    属性的功能,在属性内部进行一系列的逻辑计算,最终将计算结果返回。

python3.x以后只有新式类,不涉及经典类,所以这里只讨论新式类的@property装饰器。

#-*- coding:utf-8 -*-
#Author:'Yang'

class Goods:
    def __init__(self):
        self.original_price=100
        self.dicount=0.75

    @property
    def price(self):
        print("@property")
        new_price=self.original_price*self.dicount
        return  new_price

    @price.setter
    def price(self,value):
        print("@price.setter")
        self.original_price=value


    @price.deleter
    def price(self):
        print("@price.deleter ")
        del self.original_price


obj=Goods()

orig=obj.price  #获取商品价格
print(orig)
after=obj.price=123 #修改商品原价
print(after)
del obj.price   #删除商品原价
obj.price   #会报错:AttributeError: 'Goods' object has no attribute 'original_price',因为上一句已经进行了删除
@property的访问方法

 

再来说说属性的两种定义,

一种是,在方法上应用装饰器 @property,这种方式刚才已经接触过了;

另一种是,在类中定义值为property的对象的静态字段

#-*- coding=utf-8 -*-
class ClassName:
    def __init__(self):
        pass
    def func(self):
        # print("普通方法",self)
        return True

    #定义属性
    flags=property(func)

#调用属性
p=ClassName()
result=p.flags#自动调用func方法,并获取方法的返回值
print(result)
属性的另一种定义方式
#-*- coding:utf-8 -*-
#Author:'Yang'

class Goods:
    def __init__(self):
        self.original_price=100
        self.dicount=0.75

    def get_price(self):
        # print("@property")
        new_price=self.original_price*self.dicount
        return  new_price

    def set_price(self,value):
        # print("@price.setter")
        self.original_price=value

    def del_price(self):
        # print("@price.deleter ")
        del self.original_price

    PRICE=property(get_price,set_price,del_price)

obj=Goods()

print(obj.PRICE)  #获取商品价格
obj.PRICE=123 #修改商品原价
print(obj.PRICE)
del obj.PRICE   #删除商品原价
obj.PRICE   #会报错:AttributeError: 'Goods' object has no attribute 'original_price',因为上一句已经进行了删除
property静态字段的访问方法

 

2.类成员的修饰符                                                                                                                                                        

 对每一个类而言,都有两种形式:

  公有成员,在任何地方都能访问;私有成员命名时,前两个字符是下划线

  私有成员,只有在类的内部才能访问。

先来说说静态字段

  公有静态字段——类可以直接访问;类内部可以访问;派生类中也可以访问。

#-*- coding=utf-8 -*-

class C:
    name="公有静态字段"

    def func(self):
        print("func",C.name)

class D(C):
    def show(self):
        print("show",C.name)

C.name  #类可以访问

obj=C()
obj.func()  #类的内部可以访问

obj_son=D()
obj_son.show()  #派生类中也可以访问
公有静态字段的访问

  私有静态字段——仅仅类内部可以访问。

#-*- coding:utf-8 -*-

class C:
    __name="私有静态字段"

    def func(self):
        print("func",C.__name)

class D(C):
    def show(self):
        print("show",C.__name)



obj=C()
obj.func()  #类的内部可以访问

C.__name  #类无法访问,报错

obj_son=D()
obj_son.show()  #派生类中也无法访问,报错
私有静态字段的访问

然后来看看普通字段:

  公有普通字段——对象可以访问;类内部可以访问;派生类中也可以访问。

#-*- coding:utf-8 -*-
#Author:'Yang'

class C:
    def __init__(self):
        self.var="公有普通字段"

    def func(self):
        print("func",self.var)

class D(C):
    def show(self):
        print("show",self.var)

obj=C()     #创建实例化对象
res=obj.var     #通过对象访问
print(res)

obj.func()  #类的内部可以访问

obj_son=D()
obj_son.show()  #派生类中也可以访问
公有普通字段的访问

  私有普通字段——仅仅类内部可以访问。

#-*- coding:utf-8 -*-
#Author:'Yang'

class C:
    def __init__(self):
        self.__var="私有普通字段"

    def func(self):
        print("func",self.__var)

class D(C):
    def show(self):
        print("show",self.__var)

obj=C()     #创建实例化对象
obj.func()  #类的内部可以访问

res=obj.__var     #通过对象无法访问,报错
print(res)

obj_son=D()
obj_son.show()  #派生类中也无法访问,报错
私有普通字段的访问

注意:如果要强制访问私有字段,可以通过 对象._类名__私有字段名 (如obj._C__var)进行访问,但是不建议强制访问私有成员。

再来讲讲方法:

  公有方法——在任何地方都可以被访问。

  私有方法——这里分为单下划线的方法(函数)和双下划线的方法(函数),两者在调用时区别很大:

        单下划线的函数,在类中可以访问和调用类的实例对象可以直接访问,派生类也可以访问

        双下划线的函数,在类中可以访问和调用但类的实例对象无法访问派生类无法访问

#-*- coding:utf-8 -*-
#Author:'Yang'

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

    #在公有方法中调用双下划线私有方法、单下划线私有方法
    def func(self):
        print("公有方法")
        ClassName.__func(self)
        ClassName._func(self)
    #单下划线私有方法
    def _func(self):
        print("单下划线,私有方法")
    #双下划线私有方法
    def __func(self):
        print("双下划线,私有方法")


#创建实例对象
obj=ClassName()
print("在公有方法中可以调用双下划线私有方法、单下划线私有方法".center(50,"*"))
obj.func()
类中的访问方式:公有调用私有
#-*- coding:utf-8 -*-
#Author:'Yang'

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

    #公有方法
    def func(self):
        print("公有方法")

    #在单下划线私有方法中调用双下划线私有方法、公有方法
    def _func(self):
        print("单下划线,私有方法")
        ClassName.__func(self)
        ClassName.func(self)

    def __func(self):
        print("双下划线,私有方法")


#创建实例对象
obj=ClassName()
print("在单下划线私有方法中可以调用双下划线私有方法、公有方法".center(50,"*"))
obj._func()
类的访问方式:单下划线调用双下划线和公有
#-*- coding:utf-8 -*-
#Author:'Yang'

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

    #公有方法
    def func(self):
        print("公有方法")
    #单下划线私有方法
    def _func(self):
        print("单下划线,私有方法")
    #在双下划线私有方法中调用单下划线私有方法、公有方法
    def __func(self):
        print("双下划线,私有方法")
        ClassName._func(self)
        ClassName.func(self)

    def foo(self):
        ClassName.__func(self)

#创建实例对象
obj=ClassName()
print("在双下划线私有方法中可以调用单下划线私有方法、公有方法".center(50,"*"))
# obj.__func()#报错:AttributeError: 'ClassName' object has no attribute '__func'
obj.foo()
类中的访问方式:双下划线调用单下划线和公有

注意:以上调用仅适用在类定义中

#-*- coding:utf-8 -*-
#Author:'Yang'

class ClassName(object):
    def __init__(self):
        pass
    def func(self):
        print("公有方法")

    def _func(self):
        print("单下划线,私有方法")

class D(ClassName):
    def __init__(self):
        ClassName.__init__(self)
        pass
    def show(self):
        ClassName.func(self)
        ClassName._func(self)

print("实例对象可以访问的方法".center(50,"*"))
#创建实例对象
obj=ClassName()
obj.func()
'''单下划线的函数,类的实例对象 可以 直接访问'''
obj._func()
# obj.__func()

print("派生类可以访问的方法".center(50,"*"))
obj_son=D()
obj_son.show()
公有方法和单下划线私有方法的访问
#-*- coding:utf-8 -*-
#Author:'Yang'

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

    #在公有方法中调用双下划线私有方法
    def func(self):
        print("公有方法")
        ClassName.__func(self)
    #在单下划线私有方法中调用双下划线私有方法
    def _func(self):
        print("单下划线,私有方法")
        ClassName.__func(self)

    '''双下划线的函数或属性,在类定义中可以调用和访问,类的实例实例 无法 访问,派生类也 无法 访问'''
    def __func(self):
        print("双下划线,私有方法")

class D(ClassName):
    def __init__(self):
        ClassName.__init__(self)
        pass
    def show(self):
        '''双下划线的函数,派生类 无法 访问'''
        ClassName.__func(self)  #报错:AttributeError: type object 'ClassName' has no attribute '_D__func'


#创建实例对象
obj=ClassName()
print("在公有方法中调用双下划线私有方法".center(50,"*"))
obj.func()
print("在单下划线私有方法中调用双下划线私有方法".center(50,"*"))
obj._func()
'''双下划线的函数,类的实例实例 无法 访问'''
obj.__func()    #报错:AttributeError: 'ClassName' object has no attribute '__func'

obj_son=D()
obj_son.show()
双下划线私有方法的访问

最后提一下属性,完全可以参考普通字段部分

3.类的特殊成员                                                                                                                                                            

 所谓类的特殊成员,就是指不同于类成员:字段、方法和属性的其他成员。下面就来认识下,这些特殊成员们:

1)__init__

构造函数

2)__del__

析构函数

3)__doc__

查看类的描述信息

4)__module__

查看当前操作的对象在哪个模块

5)__class__

查看当前操作的对象的所属类

6)__dict__

查看类或对象中的所有成员

7)__call__

在类中定义def __call__(self,*args, **kwargs),在实例化对象时,就可以直接在对象后面加括号,触发执行。

例如,构造方法def __init__(self)的执行是由创建实例化对象触发的,即 对象=类名();

     __call__方法的执行是由对象后加括号触发的,即 对象() 或 类名()()

8)__str__

在类中定义def __str__(self),在实例化对象时,就可以通过打印 对象,输出__str__方法的返回值。

9)__new__、__metaclass__

在讲__new__、__metaclass__方法之前,让我们先来理解“python中一切事物皆对象”这句话。

#-*- coding:utf-8 -*-

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

obj=ClassName()     #obj是通过ClassName类实例化的对象
print(type(obj))    #输出<class '__main__.ClassName'>,表示obj对象由ClassName这个类创建
print(type(ClassName))      #输出<class 'type'>,表示ClassName这个类由type类创建

执行上述代码后,你再理解“python中一切事物皆对象”这句话:obj对象通过执行 ClassName这个类的构造方式创建的,而 ClassName这个类又是通过执行 type这个类的构造方法创建的。

也就是说,我们平时创建的类,是由type类实例化而产生的。

Python中类的实例化是由Python解释器先后调用__new____init__这两个魔法方法来完成的。

__new__方法用来完成实例化后的对象的“骨架”(比如,解释器会为对象分配地址,并返回一个指向该对象的引用值,该引用值会被紧接着传递给__init__函数)

__init__方法用“ self.属性名 = 属性值 ”这样的方式对实例化的对象进行“填充”。

那么你一定会想深究,type类又是如何实现类的创建的呢?类的内部又是怎样创建对象的呢?

先来解答前半段问题,看看下面用普通方式创建类

#-*- coding:utf-8 -*-
#Author:'Yang'

#普通方式创建类
class ClassName(object):
    def __init__(self,name):
        print("自动触发构造函数")
        self.name=name
    def func(self):
        print("hello %s." %self.name)

obj=ClassName("Jack")     #obj是通过ClassName类实例化的对象
obj.func()
普通方式创建类

其实上述代码还有一种特殊的方式,直接用type类创建类;本质上普通方式创建类也是通过type类创建的,只是被封装在内部。

#-*- coding:utf-8 -*-
#Author:'Yang'

#特殊方式(type类创建)
def func(self):
    print("hello %s." %self.name)
def __init__(self,name):
    print("自动触发type类创建的构造函数")
    self.name=name

ClassName_type=type("ClassName_type",(object,),{'func':func,'__init__':__init__})

obj_type=ClassName_type("Jack")
obj_type.func()
特殊方式直接用type类创建

以上两种类的创建方式,达到的效果是完全一样的,实例化对象的输出结果也是一致的。

现在让我们来揭开后半段问题的谜底:

  元类:__metaclass__

什么是元类呢?元类就是用来创建这些类(对象)的,是类的类。type其实就是python的内建元类。

我们可以在写一个类的时候,像这样为这个类添加__metaclass__属性。

class ClassName(object):
    __metaclass__=something
[...]

这样做了,Python就会用元类来创建类ClassName。小心点,这里面有些技巧。执行到class ClassName(object),类对象ClassName还没有在内存中创建。这时,Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用__metaclass__属性来创建类ClassName,如果没有找到__metaclass__属性,就会用内建的type来创建类ClassName。

如果是派生类,像下面这样:

class ClassName(BaseClass):
    pass

Python又是怎么创建类的呢?

Python先在ClassName中查找是否有__metaclass__属性,如果有,Python会在内存中通过__metaclass__创建一个名为ClassName的对象;如果没有,Python会继续到BaseClass(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作;如果在任何父类中都没找到__metaclass__属性,Python就会在模块层次中寻找__metaclass__属性,并尝试在上述同样的操作;如果还是找不到__metaclass__属性,Python就会有用内置的type来创建这个对象。

了解了上述原理,问题又来了,something到底可以放些什么样的代码呢?

答:可以放置创建一个类的东西。

什么可以用来创建一个类呢?

答:type类,或 任何使用到type或子类化type的东西。这可能就要用到自定义元类了。

什么是自定义元类?

答:元类的目的主要是为了在创建类时能够自动的拦截类、改变类、返回类。

  通常通常,你会为API做这样的事情,你希望可以创建符合当前上下文的类。

  假想一个很傻的例子,你希望在你的模块里所有类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就好了。

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__=upper_attr    # 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
    bar="bip"   #公有静态属性字段

print(hasattr(Foo,'bar'))
print(hasattr(Foo,'BAR'))

f=Foo()
print(f.BAR)
View Code

上面我们使用了函数做元类传递给类,下面我们使用一个正式类来作为元类传递给元类__metaclass__。

class UpperAttrMataclass(type):
    def __new__(cls,name,bases,dct):
        '''返回一个类对象,将属性都转为大写形式'''
        # __new__ 是在__init__之前被调用的特殊方法
        # __new__是用来创建对象并返回之的方法
        # 而__init__只是用来将传入的参数初始化给对象
        # 你很少用到__new__,除非你希望能够控制对象的创建
        # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
        # 如果你希望的话,你也可以在__init__中做些事情
        # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
        attrs=((name,value) for name,value in dct.items() if not name.startswith('__')) #选择所有不以'__'开头的属性
        uppercase_attr=dict((name.upper(),value) for name,value in attrs)        #将它们装换为大写形式
        return super(UpperAttrMataclass,cls).__new__(cls,name,bases,uppercase_attr)    #new是用来创建实例的

class Foo(object):
    __metaclass__=UpperAttrMataclass
    bar="bip"   #公有静态属性字段

print(hasattr(Foo,'bar'))
print(hasattr(Foo,'BAR'))

f=Foo()
print(f.BAR)
View Code

注:以上关于元类__metaclass__的代码仅在python2.7中验证。在python3.x中无法成功。

10)__getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据。

11)__getslice__、__setslice__、__delslice__

这三个方法用于分片操作,如:列表   slice  : 切片 切割。

12)__iter__

用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__。

 

posted on 2017-06-28 16:16  奔跑的蜗牛~~  阅读(94)  评论(0)    收藏  举报

导航