2018-01-22 python面向对象基础(二)

类的成员:

成员有以下:

1、字段: 可分为静态字段 普通字段
2、方法: 可分为静态方法 类方法 普通方法
3、特性/属性 普通属性

注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。

一、字段

静态字段在内存中只保存一份。
普通字段在每个对象中都要保存一份。

类可以直接访问静态字段,不能直接访问普通字段。

二、方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
静态方法:由类调用;无默认参数;

三、属性

对于属性property的使用方法

class Foo:
    def __init__(self):
        pass

    @property
    def property_func(self):
        return 'xxx'

f = Foo()
f.property_func  ###属性的调用  不需要加括号

注意:

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

属性用法一、

class A:


    def func(self):
        return 'xxx'

    BAR = property(func)

a = A()
res = a.BAR     #####自动调用property中的个的func方法,并获取返回值
print(res)

属性的三种用法(新式类):

以下来Python源码

        property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
        
        fget is a function to be used for getting an attribute value, and likewise
        fset is a function for setting, and fdel a function for del'ing, an
        attribute.  Typical use is to define a managed attribute x:
class C(object):
    def getx(self): return self._x

    def setx(self, value): self._x = value

    def delx(self): del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")


# Decorators make defining new properties or modifying existing oneseasy:


class C(object):
    @property
    def x(self):       #####获取属性
        "I am the 'x' property."
        return self._x

    @x.setter
    def x(self, value):      ###设置值时,一定要有两个参数。
        self._x = value

    @x.deleter           ##删除属性
    def x(self):  
        del self._x

Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性
请看以下代码

class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        path_info = get_path_info(environ)
        if not path_info:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            path_info = '/'
        self.environ = environ
        self.path_info = path_info
        # be careful to only replace the first slash in the path because of
        # http://test/something and http://test//something being different as
        # stated in http://www.ietf.org/rfc/rfc2396.txt
        self.path = '%s/%s' % (script_name.rstrip('/'),
                               path_info.replace('/', '', 1))
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        self.content_type, self.content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
        if 'charset' in self.content_params:
            try:
                codecs.lookup(self.content_params['charset'])
            except LookupError:
                pass
            else:
                self.encoding = self.content_params['charset']
        self._post_parse_error = False
        try:
            content_length = int(environ.get('CONTENT_LENGTH'))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get('wsgi.url_scheme')

    @cached_property
    def GET(self):
        # The WSGI spec says 'QUERY_STRING' may be absent.
        raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
        return http.QueryDict(raw_query_string, encoding=self._encoding)

    def _get_post(self):               ##############################
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    def _set_post(self, post):       ##################################
        self._post = post

    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
        return http.parse_cookie(raw_cookie)

    @property
    def FILES(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    POST = property(_get_post, _set_post)     ###########################

isinstance 和 issubclass

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

class Foo(object):
     pass
  
obj = Foo()
  
isinstance(obj, Foo)

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
issubclass(Bar, Foo)

strrepr

直接上代码

class Foo(object):
    def __init__(self,name):

        self.name = name

    def __str__(self):
        return '%s str method in class Foo'%self.name

    def __repr__(self):
        return '%s  repr method in class Foo'%self.name

a = Foo('egon')

print('%s in Foo'%a)
print('%r in Foo'%a)

注意:
1、在一个类中,如果这两种方法都存在,在执行打印实例对象的时候将会执行str
2、在语法糖的帮助下,%r 将会调用repr方法。

反射(自省):

class Foo:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say_hi(self):
        print('hi,%s'%self.name)

obj=Foo('egon',73)

#获取属性   getattr
print(getattr(obj,'asdf','doesn\'t exist'))     ####若对象中的属性不存在,则打印最后后面的结果。

#检测是否含有属性 hasattr
print(hasattr(obj,'name'))           #注意,第二个参数要是字符串。
print(hasattr(obj,'say_hi'))

# 为对象设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name + 'sb')
print(obj.__dict__)
print(obj.show_name(obj))

#删除对象属性
delattr(obj,'age')
delattr(obj,'sdf')   ###  属性不存在则报错

反射也适用于类

class Foo(object):
    staticField = "old boy"
    def __init__(self):
        self.name = 'wupeiqi'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'
print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))

getitemsetitemdelitem

class Bar(object):
    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)


obj = Bar()

result = obj['k1']       # 自动触发执行 __getitem__
obj['k2'] = 'parker'      # 自动触发执行 __setitem__
del obj['k1']               # 自动触发执行 __delitem__

绑定方法与非绑定方法

类中定义的函数分成两大类

绑定方法(绑定给谁,谁来调用就自动将他本身当作第一个参数传入)

1、绑定到类的方法:用classmethod装饰器装饰的方法,为类量身定制类. bound_method(), 自动将类当作第一个参数传入,(其实对象也能调用,但仍将类当作第一个参数传入)
2、绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制。对象.bound_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值这么一说)

非绑定方法:用staticmethod装饰器装饰的方法

1、不与类或对象绑定,类和对象都能调用,但是没有自动传值这么一说,就是一个普通工具而已
注意:与绑定到对象的方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

class

How to get the type of an object without using the type() function?

a = '111'
print(a.__class__)

slots

1.slots是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的dict属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用slots:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用slots取代实例的dict
当你定义slots后,slots就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在slots中列出的属性名在内部被映射到这个数组的指定小标上。使用slots一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在slots中定义的那些属性名。
4.注意事项:slots的很多特性都依赖于普通的基于字典的实现。另外,定义了slots后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到 的用作数据结构的类上定义slots比如在程序中需要创建某个类的几百万个实例对象 。
关于slots的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用slots可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。

class A:
    __slots__ = ['Parker','Zhang']


    def __init__(self):
        pass

a = A()

a.Parker = 1
# a.zhang = 12   ##无法给实例添加新属性

# print(a.__dict__)   #报错
print(a.__slots__)    ######['Parker', 'Zhang']

总结: slots的好处: 节省内存,由slots代替dict

enterexit (上下文管理器)

官网链接:
https://docs.python.org/2/library/stdtypes.html

Python’s with statement supports the concept of a runtime context defined by a context manager. This is implemented using two separate methods that allow user-defined classes to define a runtime context that is entered before the statement body is executed and exited when the statement ends.

An example of a context manager that returns itself is a file object. File objects return themselves from enter() to allow open() to be used as the context expression in a with statement.

Exit the runtime context and return a Boolean flag indicating if any exception that occurred should be suppressed. If an exception occurred while executing the body of the with statement, the arguments contain the exception type, value and traceback information. Otherwise, all three arguments are None.

Returning a true value from this method will cause the with statement to suppress the exception and continue execution with the statement immediately following the with statement. Otherwise the exception continues propagating after this method has finished executing. Exceptions that occur during execution of this method will replace any exception that occurred in the body of the with statement.

The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed successfully and does not want to suppress the raised exception. This allows context management code (such as contextlib.nested) to easily detect whether or not an __exit__() method has actually failed.

代码:

class Open(object):
    def __init__(self,name):
        self.name = name

    def __enter__(self):
        print('enter 方法')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit 方法') #######4来到__exit__方法
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True  ####return True 后 ,with后面的代码会正常执行,打印 'the final'

with Open('a.txt') as f:         ####1.首先进入__enter__方法
    print('with 方法')            #####2.来到with 方法
    sss ######3.抛出异常  (要是不抛出异常,exit方法中的三个参数值为None)
print('the final')

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

实际应用:

class Open(object):
    def __init__(self,name,mode):
        self.name = name
        self.mode = mode

    def __enter__(self):
        self.opened = open(self.name,self.mode)   # 在enter方法中打开文件并返回文件名称
        return self.opened  # 返回值就是as后面的writer

    def __exit__(self, exc_type, exc_val, exc_tb): #在 close方法中关闭文件
        self.opened.close()


with Open('mytext.txt','w') as writer:
    writer.write('hello world')

装饰器实现上下文管理

from contextlib import contextmanager

@contextmanager
def open_file(filename,mode):
    try:
        f = open(filename,mode)
        yield f
    finally:
        f.close()

with open_file('bbb.txt','w') as f:
    f.write('I am so awesome')

print(f.closed)

iter方法

类实例化出的对象本身不可被循环,当在父类中加入iter方法后,遍历一个对象将会执行此方法:

class Reverse(object):

    def __init__(self,data):
        self.data = data

    def __iter__(self):
        for i in self.data:
            yield i

data = Reverse([1,2,3,4])
for datum in data:
    print(datum)
class Reverse(object):
    def __init__(self,data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]
rev = Reverse([1,2,3,4])
i = iter(rev)
for char in i:
    print(char)

判断函数与方法的区别:

from types import FunctionType,MethodType

class Foo(object):

    def __init__(self):
        pass

    def func(self):
        pass


obj = Foo()
print(isinstance(obj.func,MethodType))          #True
print(isinstance(obj.func,FunctionType))        #False

print(isinstance(Foo.func,MethodType))      #False
print(isinstance(Foo.func,FunctionType))    #True

方法,无需传入self参数

函数,必须手动传self参数

元类

class MyType(type):

    def __init__(self, *args,**kwargs):
        print(11)
        super().__init__(self)

    def __call__(self, *args, **kwargs):
        print(22)
        obj = self.__new__(self, *args, **kwargs)
        self.__init__(obj,*args)
        return obj


class Foo(metaclass=MyType):

    def __init__(self, name):
        print(44)
        self.name = name

    def __new__(cls, *args, **kwargs):
        print(33)
        return object.__new__(cls)

# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo('Parker')
print(obj)
print(obj.name)

元类总结,Python中一切皆对象,类本质上也是对象,有type创建产生,因此上述代码可以理解为:

1、在解释器执行到class Foo时,就会触发type的init方法,因而创建Foo这个类
2、在实例化Foo时,由于Foo(),所以执行创建其类(type)的call方法,call方法将会调用init方法来来执行call方法,再由call方法来触发new方法,创建类后,开始执行Foo类的init方法,初始化对象。

2018-01-22 python面向对象基础(二)_第1张图片
image.png

isinstance 和 type

2018-01-22 python面向对象基础(二)_第2张图片
image.png

判断对象是否属于一个类时,isinstance() 不如type准确

对象之间的相加

class Foo(object):
    def __init__(self,age):
        self.age = age

    def __add__(self, other):

        return Foo(obj1.age + other.age)

obj1 = Foo(11)
obj2 = Foo(12)
obj3 = obj1 + obj2
print(obj3.age)

私有属性的方法


class A(object):

    __age = 23
    def __init__(self,name):
        self.__name = name

a = A('Parker')

print(a._A__age)  # 对象._类__字段
print(a._A__name)  

你可能感兴趣的:(2018-01-22 python面向对象基础(二))