面向对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象相关的知识,希望对你有一定的参考价值。

类的成员

类的成员可以分为三大类:字段、方法和属性

一、字段

字段包括普通字段和静态字段,他们在定义和使用中有所区别,最本质的区别是内存中保存的位置不同。

  • 普通字段属于对象,在内存中为每个对象保存一份
  • 静态字段属于类,在内存中只保存一份

一般情况下,自己访问自己字段,普通字段只能使用对象访问,静态字段使用类访问(最好不要用对象访问静态字段),静态字段在代码加载时创建。

#!/usr/bin/env python
# coding=utf-8


class Province(object):

    # 静态字段,保存在类中
    country = \'中国\'

    def __init__(self, province):
        # 普通字段,保存在对象中
        self.name = \'province\'
    # 普通方法,由对象去调用执行(方法属于类)
    def show(self):
        return self.name

    # 静态方法,由类调用执行,对象是封装数据的,用不到对象封装数据,使用静态方法
    @staticmethod
    def f1():
        return \'....\'

test = Province(\'henan\')
print(Province.country)
print(test.country)
print(test.show())

print(Province.f1())
View Code

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

二、方法

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

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
  • 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
  • 静态方法:由调用;无默认参数;
#!/usr/bin/env python
# coding=utf-8

# 面向对象中类成员
# 字段: 静态字段, 普通字段
# 方法:  普通方法 静态方法, 类方法
#      所有方法属于类
#      普通方法:至少一个self,对象执行
#      静态方法:任意参数, 类执行(对象执行,基本不要用)
#      类方法: 至少一个cls,类执行(对象执行,基本不要用)
# 属性: 看pager.py  (@property)
#       具有方法的写作形式,具有字段的访问形式

class Province(object):

    # 静态字段,保存在类中
    country = \'中国\'

    def __init__(self, province):
        # 普通字段,保存在对象中
        self.name = \'province\'
    # 普通方法,由对象去调用执行(方法属于类)
    def show(self):
        return self.name

    # 静态方法,由类调用执行,对象是封装数据的,用不到对象封装数据,使用静态方法
    # 可以有参数,可以没有
    @staticmethod
    def f1():
        return \'....\'

    # 类方法
    # 必须有一个参数cls, cls是类名  加()创建对象
    @classmethod
    def f2(cls):
        print(cls.country)
        print(cls)

test = Province(\'henan\')
print(Province.country)
print(test.country)
print(test.show())


print(Province.f1())

Province.f2()
View Code 

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

三、属性

属性定义有两种方式

  1. 装饰器 在方法上应用装饰器
    #!/usr/bin/env python
    # coding=utf-8
    
    class Pager(object):
    
        def __init__(self, page):
            self.all_count = page
    
        # 获取
        @property
        def all_pager(self):
            a1, a2 = divmod(self.all_count, 10)
            if a2 == 0:
                return a1
            else:
                return a1 + 1
        # 设置
        @all_pager.setter
        def all_pager(self, value):
            print(value)
    
        # 删除
        @all_pager.deleter
        def all_pager(self):
            print(\'del all_pager\')
    
    
    p = Pager(101)
    ret = p.all_pager  # 自动去执行Pager里面的@property下的all_pager方法,下同
    print(ret)
    
    p.all_pager = 111
    
    del p.all_pager
    View Code
  1. 静态字段 在类中定义为property对象的静态字段
     1 #!/usr/bin/env python
     2 # coding=utf-8
     3 class Pager:
     4     def __init__ (self, all_count):
     5         self.all_count = all_count
     6     def f1(self):
     7         return 123
     8     def f2(self, value):
     9         pass
    10     def f3(self):
    11         pass
    12     foo = property(fget=f1, fset=f2, fdel=f3)
    13 p = Pager(101)
    14 ret = p.foo # 执行fget
    15 print(ret)
    16 p.foo = \'hexm\'  # 执行fset
    17 del p.foo   # 执行fdel
    View Code

类成员修饰符

对于每一个类的成员而言都有两种形式:

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法
 1 class Foo(object):
 2     __cc = \'123\'    # 公有静态字段
 3 
 4     def __init__(self, name):
 5         # 私有的,只能在类内部访问
 6         self.__name = name    # 私有字段
 7 
 8     def f1(self):
 9         print(self.__name)
10 
11     def f2(self):
12         print(Foo.__cc)
13 
14 obj = Foo(\'hexm\')
15 obj.f1()
16 # print(obj.__name)  # 错误
17 print(obj._Foo__name)  # 最好不要用这种方式访问私有字段
18 
19 obj.f2()
20 print(Foo.f1__cc)
21 
22 # 继承Foo也不能访问私有的
23 class Bar(Foo):
24     def f2(self):
25         print(self.__name)
26 
27 obj = Bar(\'hexm\')
28 obj.f1()
29 obj.f2()  # 错误
View Code

类的特殊成员

上面介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:

1. __doc__

  表示类的描述信息

1 class Foo:
2     """ 描述类信息 """
3 
4     def func(self):
5         pass
6 
7 print Foo.__doc__
View Code

2. __module__ 和  __class__ 

  __module__ 表示当前操作的对象在那个模块

  __class__     表示当前操作的对象的类是什么

1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 
4 class C(object):
5 
6     def __init__(self):
7         self.name = \'hexm\'
lib/a.py
1 from lib.aa import C
2 
3 obj = C()
4 print obj.__module__  # 输出 lib.aa,即:输出模块
5 print obj.__class__      # 输出 lib.aa.C,即:输出类
index.py

3. __init__

  构造方法,通过类创建对象时,自动触发执行。

4. __del__

  析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

5.__call__

对象后面加括号,触发执行

 1 class Foo(object):
 2     def __init__(self):
 3         # 构造方法,创建对象自动执行
 4         print(\'init\')
 5     def __del__(self):
 6         # 析构方法
 7         pass
 8     def __call__(self):
 9         print(\'call\')
10 p = Foo()
11 ret = p.__class__
12 print(ret)
13 p()  # 对象后面加括号,执行__call__方法
14 Foo()()  # 先执行构造方法再执行call
View Code

6.__dict__

类或对象里面所有成员

 1 class Foo:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5     def __call__(self):
 6         return 123
 7 test1 = Foo(\'hexm\', 18)
 8 test2 = Foo(\'zhuxj\', 17)
 9 # 对象里面封装的字段
10 ret = test1.__dict__
11 print(ret)
12 # 类里面所有成员
13 print(Foo.__dict__)
View Code

7.__str__

在类中定义了__str__方法,在打印对象时,默认输出该方法的返回值

 1 #!/usr/bin/env python
 2 # coding=utf-8
 3 class Foo:
 4     def __init__(self, name, age):
 5         self.name = name
 6         self.age = age
 7     def __str__(self):
 8         #  print 对象时,自动执行该方法
 9         return \'%s---%s\' % (self.name, self.age)
10 test1 = Foo(\'hexm\', 18)
11 test2 = Foo(\'zhuxj\', 17)
12 print(test1)
13 print(test2)
14 ret1 = str(test1)
15 ret2 = str(test2)
16 print(ret1)
17 print(ret2)
View Code

8.__getitem__、__setitem__、__delitem__

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

 1 #!/usr/bin/env python
 2 # coding=utf-8
 3 class Foo:
 4     def __init__(self, name, age):
 5         self.name = name
 6         self.age = age
 7     def __str__(self):
 8         #  print 对象时,自动执行该方法
 9         return \'%s---%s\' % (self.name, self.age)
10     def __getitem__(self, item):
11         if type(item) == slice:
12             print(item.start)
13             print(item.stop)
14             print(item.step)
15         elif type(item) == str:
16             print(item)
17     def __setitem__(self, key, value):
18      #   print(type(key), type(value))
19         if type(key) == slice:
20             print(key,value)
21     def __delitem__(self, key):
22         print(type(key))
23 test1 = Foo(\'hexm\', 18)
24 test1[\'sb\']   # 执行__getitem__  字符串类型
25 test1[1:3:1]    # 执行__getitem__   slice类型
26 test1[\'k1\'] = \'hexm\'  # 执行 __setitem__
27 test1[1:4] = [1, 2, 3, 4]
28 del test1[\'k1\']  # 执行 __delitem__
29 del test1[1:4]
View Code

9.__iter__

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

1 #!/usr/bin/env python
2 # coding=utf-8
3 class Foo:
4     def __iter__(self):
5         return iter([1, 2, 3, 4])
6 test = Foo()
7 #  把对象放到for循环中,会自动执行__iter__方法, 返回的值得可迭代对象
8 for item in test:
9     print(item)
View Code

10. __add__, __del__

在类中定义__add__, __del__时,执行两个对象相加,执行__add__,两个对象相减,执行__del__

1 def __add__(self, lover):
2     xxx
View Code

11. issbuclass, isinstance

 1 #!/usr/bin/env python
 2 # coding=utf-8
 3 class Foo:
 4     def __iter__(self):
 5         return iter([1, 2, 3, 4])
 6 
 7 class Fo(Foo):
 8     pass
 9 test = Foo()
10 # 对象是否属于某个类
11 ret = isinstance(test, Foo)
12 print(ret)
13 ret = isinstance(test, Fo)
14 print(ret)
15 # 一个类是否是另一个类的子类
16 ret = issubclass(Fo, Foo)
17 print(ret)
View Code

12.__enter__

python使用with-as语法,就会调用__enter__函数,然后把函数的return值传给as后指定的变量。然后执行with-as下的语句,无论出现了什么异常,都会在离开时执行__exit__

为了让一个对象兼容with语句,需要实现__enter__()__exit__()方法。

 1 from socket import socket, AF_INET, SOCK_STREAM
 2 class LazyConnection:
 3     def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
 4         self.address = address
 5         self.family = family
 6         self.type = type
 7         self.connections = []
 8     def __enter__(self):
 9         sock = socket(self.family, self.type)
10         sock.connect(self.address)
11         self.connections.append(sock)
12         return sock
13     def __exit__(self, exc_ty, exc_val, tb):
14         self.connections.pop().close()
15 # Example use
16 from functools import partial
17 conn = LazyConnection((\'www.python.org\', 80))
18 with conn as s1:
19     pass
20     with conn as s2:
21         pass
22         # s1 and s2 are independent sockets
View Code

 

创建大量对象时节省内存方法

如果程序要创建(上百万)大量对象,导致占用很大内存。可以给类添加__slots__属性来极大减少实例所占的内存。使用slots后不能给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

比如:

#!/usr/bin/env python
# coding=utf-8

class Date(objct):
    __slot__ = [\'year\', \'month\', \'day\']
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

 

 

 

以上是关于面向对象的主要内容,如果未能解决你的问题,请参考以下文章

面向面试编程代码片段之GC

PHP面向对象之选择工厂和更新工厂

Java中面向对象的三大特性之封装

python之路之前没搞明白4面向对象(封装)

Scala的面向对象与函数编程

Python面向对象学习之八,装饰器