Python3 与 C# 面向对象之~封装

1.8 实例方法、类方法、静态方法

先说说 实例方法,实例方法第一个定义的参数只能是实例本身引用self,只能通过实例调用(就是我们之前用的 def func_name(self,xxx):

类方法:是类对象所拥有的方法,需要用修饰器@classmethod来标识,第一个参数必须是类对象cls,可以通过类或者实例直用

静态方法:定义静态方法使用装饰器@staticmethod,没有默认的必须参数,通过类和实例直接调用

静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过 类对象来引用(访问)

小明眼花缭乱的对老师说道,老师给我看几个案例吧:

In [1]:

class Dog(object):
    # 类属性
    name = "小汪"

    # 实例方法
    def __init__(self, age):
        # 实例属性
        self.age = age
        # 打印看看
        print("self id:%s" % id(self))

    # 类方法
    @classmethod
    def show_name(cls):
        # 访问类属性 cls.xxx
        print("我叫%s" % cls.name)
        # 打印看看
        print("cls id:%s" % id(cls))

    # 静态方法
    @staticmethod
    def say_hello():
        print("汪汪汪")


def main():
    # 类名方式访问
    Dog.show_name()
    Dog.say_hello()  # 类名的方式可以访问静态方法

    # 实例对象方式访问
    dog = Dog(2)
    dog.show_name()
    dog.say_hello()


if __name__ == '__main__':
    main()

 

我叫小汪
cls id:94310818174200
汪汪汪
self id:140392216464016
我叫小汪
cls id:94310818174200
汪汪汪

 


一般都是这样用的(供参考):

实例方法:一般平时用的都是它

类方法:类方法用在模拟C#多个构造函数(Python里面不能有同名函数) or 你需要 对类属性、类方法操作之类的

静态方法:一般 都是独立功能,类似于函数,只不过在面向对象里面一般这么用

 


3. 内部类

  • 所谓内部类,就是在类的内部定义的类,主要目的是为了更好的抽象现实世界。
  • 例子:
    汽车是个类,汽车的底盘,轮胎也可以抽象为类,将其定义到汽车类中,则形成内部类,更好的描述汽车类,因为底盘、轮胎是汽车的一部分。
  • 方法1:直接使用外部类调用内部类
    object_name = outclass_name.inclass_name()
class People(object):
    color = 'yellow'
    class Chinese(object):
        name = '内部类'
    def think(self):
        print "I am a thinker"
ren = People.Chinese()
print ren.name

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
内部类
  • 方法2:先对外部类进行实例化,然后再实例化内部类
    out_name = outclass_name()
    in_name = out_name.inclass_name()
    in_name.method()
class People(object):
    color = 'yellow'
    class Chinese(object):
        name = '内部类'
    def think(self):
        print "I am a thinker"
ren = People()  # 在People里取对象
tom = ren.Chinese()  # 在Chinese再取一个对象
print tom.name

也可换成:

class People(object):
    color = 'yellow'
    class Chinese(object):
        name = '内部类'
    def think(self):
        print "I am a thinker"
# ren = People()
# tom = ren.Chinese()
# print tom.name
print People.Chinese.name

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
内部类

魔术方法:

  • _str_(self)
  • 构造函数与析构函数
    • 构造函数:
      用于初始化类的内部状态,Python提供的构造函数是_init_();
      _init_()方法是可选的,如果不提供,Python会给出一个默认的_init_方法
    • 析构函数:
      用于释放对象占用的资源,Python提供的析构函数是_del_();
      _del_()也是可选的,如果不提供,则Python会在后台提供默认析构函数
class People(object):
    color = 'yellow'
    class Chinese(object):
        name = '内部类'
    def think(self):
        print "I am a thinker"
    def __str__(self):
        return '这是People类'  # 只能用return,不能用print
    def __init__(self):
        self.color = 'black'
    def __del__(self):
        print 'del..'
ren = People()
print ren
print ren.color
print People.color

_del_(self)在程序最后执行,比如程序执行完关闭某文件。不手动关闭的话,Python也会自动回收。

  • Python采用垃圾回收机制来清理不再使用的对象;python提供gc模块释放不再使用的对象。
  • Python采用”引用计数”的算法方式来处理回收,即:当某个对象在其作用域内不再被其他对象引用的时候,python就自动清除对象;
  • gc模块的collect()可以一次性收集所有待处理的对象(gc.collect)

在线编程https://mybinder.org/v2/gh/lotapp/BaseCode/master
在线预览:http://github.lesschina.com/python/base/oop/1.封装.html

2.类的方法

  • 方法的定义和函数一样,但是需要self作为第一个参数。
    类方法为:

    • 公有方法
    • 私有方法
    • 类方法
    • 静态方法
  • 公有方法:在类中和类外都能调用的方法。

  • 私有方法:不能被类的外部调用,在方法前面加上”__”双下划线就是私有方法。

  • self参数
    用于区分函数和类的方法(必须有一个self),self参数表示执行对象本身。

class People(object):
    color = 'yellow'  
    def think(self):    # 公有方法
        print "I am a thinker"
    def f(self):
        self.think()  # 内部调用公有方法
ren = People()
ren.think()  
ren.f()  #外部调用公有方法

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
I am a thinker
I am a thinker

看下私有方法:

class People(object):
    color = 'yellow'    
    def think(self):    
        print "I am a thinker"
    def __f(self):  # 私有方法
        self.think()    #内部调用公有方法
ren = People()
ren.think()
ren.__f()   #外部不可调用私有方法

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
I am a thinker
Traceback (most recent call last):
  File "C:/Users/chawn/PycharmProjects/pyex/180108/1.py", line 13, in <module>
    ren.__f()
AttributeError: 'People' object has no attribute '__f'
  • 类方法:被classmethod()函数处理过的函数,能被类所调用,也能被对象所调用(是继承的关系)
  • 静态方法:相当于”全局函数”,可以被类直接调用,可以被所有实例化对象共享,通过staticmethod()定义,静态方法没有”self”参数。
    装饰器:
    • @classmethod
    • @staticmethod
class People(object):
    color = 'yellow'
    def think(self):
        print "I am a thinker"
    def __f(self):  # 私有方法
        self.think()    #内部调用公有方法
    def test(self):
        print 'ok'
    cm = classmethod(test)  #处理一下方法

ren = People()
People.cm()  #通过类访问被处理过得方法,没被处理的不调用
People.think()

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
ok
 File "C:/Users/chawn/PycharmProjects/pyex/180108/1.py", line 17, in <module>
    People.think()
TypeError: unbound method think() must be called with People instance as first argument (got nothing instead)
# think方法没有被classmethod处理,所以类不能调用

静态方法()里为空,不能直接被对象或者类调用,要先用staticmethod处理。而且正因为没有self参数,所以不能调用其他方法,只能调用属性。相比之下,作用很静态。

class People(object):
    color = 'yellow'
    def think(self):
        print "I am a thinker"
    def __f(self):  # 私有方法
        self.think()    #内部调用公有方法
    def test(): # 静态方法
        print 'ok'

ren = People()
People.cm()

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
Traceback (most recent call last):
  File "C:/Users/chawn/PycharmProjects/pyex/180108/1.py", line 15, in <module>
    People.cm()
AttributeError: type object 'People' has no attribute 'cm'

class People(object):
    color = 'yellow'
    def think(self):
        print "I am a thinker"
    def __f(self):  # 私有方法
        self.think()    #内部调用公有方法
    def test(): # 静态方法
        print 'ok'
    cm = staticmethod(test)
ren = People()
People.cm()

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
ok

装饰器,有装饰器之后,类可以直接访问:

class People(object):
    color = 'yellow'
    def think(self):
        print "I am a thinker"
    def __f(self):  # 私有方法
        self.think()    #内部调用公有方法

    @classmethod
    def test(self): # 动态方法
        print 'haha'

    @staticmethod
    def test1():    #静态方法
        print 'ok'

ren = People()
People.test()
People.test1()

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
haha
ok

这次尽量用故事模式来讲知识,上次刚说美化,这次算是第一篇了。步入正题:

1.类的属性

创建对象的过程称之为实例化;当一个对象被创建后,包含三个方面的特性:对象句柄、属性和方法。
句柄用于区分不同的对象
对象的属性和方法与类中的成员变量和成员函数对应
obj = MyClass() //创建类的一个实例(对象)
通过对象来调用方法和属性

  • 类的属性按使用范围分为公有属性和私有属性,类的属性范围取决于属性的名称。
  • 公有属性:在类中和类外都能调用的属性。
    • 私有属性:不能在类外及被类以外的函数调用。
      定义方式:以”__”双下划线开始的成员变量就是私有属性
      可以通过instance._classname__attribute方式访问。第一个下划线只有一个,第二个下划线是2个
#!/usr/bin/python
# -*- coding:utf8 -*-
# author: chawn
# date:
class People(object):
    color = 'yellow'    # 公有属性
    __age = 30      # 定义私有属性
    def think(self):
        self.color = 'black'    # 调用公有属性
        print self.__age        # 调用私有属性
        print "I am a %s" % self.color
        print "I am a thinker"

ren = People()
print ren.color
ren.think()
print ren.__age

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
yellow
30
I am a black
I am a thinker
Traceback (most recent call last):
  File "C:/Users/chawn/PycharmProjects/pyex/180108/1.py", line 17, in <module>
    print ren.__age
AttributeError: 'People' object has no attribute '__age'

私有属性在外部调用报错AttributeError: 'People' object has no attribute '__age'。
正确写法:print ren._People__age

  • 内置属性:由系统在定义类的时候默认添加的,由前后双下划线构成,_dict_, _module_
#!/usr/bin/python
# -*- coding:utf8 -*-
# author: chawn
# date:
class People(object):
    color = 'yellow'    # 公有属性
    __age = 30      # 定义私有属性
    def think(self):
        self.color = 'black'    # 调用公有属性
        print self.__age        # 调用私有属性
        print "I am a %s" % self.color
        print "I am a thinker"

ren = People()
print ren.color
ren.think()
print ren.__dict__    #输出内置属性

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
yellow
30
I am a black
I am a thinker
{'color': 'black'}  #以字典输出

通过类来调用内置属性:

#!/usr/bin/python
# -*- coding:utf8 -*-
# author: chawn
# date:
class People(object):
    color = 'yellow'    # 公有属性
    __age = 30      # 定义私有属性
    def think(self):
        self.color = 'black'    # 调用公有属性
        print self.__age        # 调用私有属性
        print "I am a %s" % self.color
        print "I am a thinker"

ren = People()
ren.color = '白色'    #给对象的属性赋值
print ren.color
ren.think()
print ren.__dict__
print '*' *30    # *重复30次
print People.color  # 通过类调用属性color

C:UserschawnPycharmProjectspyexvenvScriptspython.exe C:/Users/chawn/PycharmProjects/pyex/180108/1.py
白色  #输出对象的属性
30
I am a black
I am a thinker
{'color': 'black'}
******************************
yellow  # 类的属性输出
{'__module__': '__main__', 'color': 'yellow', '__doc__': None, '__dict__': <attribute '__dict__' of 'People' objects>, '_People__age': 30, '__weakref__': <attribute '__weakref__' of 'People' objects>, 'think': <function think at 0x03384930>}  # 类的内置属性

 

1.定义一个类

类的组成:类名、属性(没有字段)、方法

1.7 类属性、实例属性

小明问老师:“老师老师,怎么没有静态类,静态属性之类的东西呢?”

老师笑而不语道:“小家伙原来不仅仅是体重增加啊,这求知欲也是大大的增加呢 ~ 且听我慢慢道来”


类在程序里面也是对象(你姑且可以认为所有的类都类似于C#里面的静态类),而通过类实例化的对象,叫实例化对象

实例属性 --> 实例对象相互之间不共享 一般我们都是在__init__中定义

类属性(类似于C#里面的静态字段) --> 属于类对象,多个实例对象之间共享

注意一下:相同名称的实例属性将屏蔽掉类属性(尽量别同名)

类属性除了可以通过 类名.类属性 访问外,还可以直接 实例对象.类属性 (C#中抽象类和静态类是不能被实例化的)

来个案例更直观点:

In [1]:

class Person(object):
    # age为类属性
    age = 1

    def __init__(self, name):
        # name为实例属性
        self.name = name


def main():
    # 类名.类属性
    print(Person.age)
    xiaoming = Person("小明")
    # 对象.类属性
    print(xiaoming.age)


if __name__ == '__main__':
    main()

 

1
1

 


如果需要在类外 修改类属性,必须通过类对象去引用然后进行修改

如果通过实例对象去引用会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性

金沙官网线上,如果通过实例对象去引用该名称的属性,实例属性会强制 屏蔽掉类属性,即引用的是实例属性,除非del了该实例属性才能正常访问类属性

你可以理解为,Python这么做只是为了方便你获取,该怎么修改还得怎么做。来看个案例:

In [3]:

class Person(object):
    # age为类属性
    age = 1

    def __init__(self, name):
        # name为实例属性
        self.name = name


def main():
    # 类名.类属性
    print(Person.age)

    # 通过对象.类属性修改
    xiaoming = Person("小明")
    xiaoming.age = 100
    print(xiaoming.age)  # 其实,并没有修改成功,只是产生了一个同名age
    print(Person.age)  # 对吧,类属性并没有被修改

    # 通过类名修改
    Person.age = 22  # 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改
    print(xiaoming.age)  # 刚才已经创建一个同名age,所以现在显示的是刚才的值
    print(Person.age)  # 通过类名.类属性 就可以看到值被修改了

    # 如果你还是不信,可以创建一个新对象看看
    xiaopan = Person("小潘")
    print(xiaopan.age)

    # xiaoming实例对象想访问怎么办?
    # 除非del了该实例属性才能正常访问类属性
    del xiaoming.age
    print(xiaoming.age)  # 这时候访问的就是 类属性 了


if __name__ == '__main__':
    main()

 

1
100
1
100
22
22
22

 

1.4 私有属性、方法

C#、Java里面都是有访问修饰符的,Python呢?

Python规定,如果以双下划线__开头的属性或者方法就是私有的

变量名类似xxx的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量。特殊变量是可以直接访问的,不是private变量

在说私有属性前,我们来个案例说说属性不私有的弊端,eg:

小明同学学了点C#,然后学习了上面的知识,心想 ~ Python这么搞安全性呢?不行,我得构造构造,于是有了下面的代码:

In [10]:

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

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = name

    def get_age(self):
        return self.age

    def set_age(self, age):
        if age > 0:
            self.age = age
        else:
            print("age must > 0")

    def show(self):
        print("name:%s,age:%d" % (self.name, self.age))

 

小明心想,想要修改age属性,你通过set_age我就可以判断了哇,还是本宝宝聪明

这时候小潘过来了,淡淡的一笑,看我怎么破了你 ~ 看代码:

In [11]:

zhangsan = Student("张三", -20)
zhangsan.show()  # name:张三,age:-20
zhangsan.age = -1  # set_age方法形同虚设,我完全可以直接访问字段了
zhangsan.show()  # name:张三,age:-1

 

name:张三,age:-20
name:张三,age:-1

 

小潘傲气的说道~大叔,给你脸呢。我就是不去访问你设定的方法怎么滴呢?

小明急的啊,赶紧去找伟哥求经。不一会,傲气的贴出自己的New Code,心想着我私有属性都用上了还怕个毛毛:

In [12]:

class Student(object):
    def __init__(self, name, age):
        self.__name = name
        # 一般需要用到的属性都直接放在__init__里面了
        # self.__age = age
        self.set_age(age)

    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if age > 0:
            self.__age = age
        else:
            print("age must > 0")

    def show(self):
        print("name:%s,age:%s" % (self.__name, self.__age))

 

小潘冷笑道~呵呵,然后使用了上次的绝招:

In [13]:

zhangsan = Student("张三", -20)
zhangsan.__age = -1  # 同样的代码,只是属性前面加了下划线
zhangsan.show()

 

age must > 0

 

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-82c41ff46846> in <module>()
      1 zhangsan = Student("张三", -20)
      2 zhangsan.__age = -1  # 同样的代码,只是属性前面加了下划线
----> 3zhangsan.show()

<ipython-input-12-1dec32486a19> in show(self)
     22 
     23     def show(self):
---> 24print("name:%s,age:%s" % (self.__name, self.__age))

AttributeError: 'Student' object has no attribute '_Student__age'

 

这次小潘同志傻眼了,完全不能访问了啊?不行,怎么能被小明大叔笑话呢?

于是上网翻资料,国内不行就国外,外文不好就翻译,终于找到一个新破解方式:

双下划线开头的实例变量不能直接访问,是因为Python解释器对外把__age变量改成了_Studentage,所以,仍然可以通过**_Studentage**来访问:

In [14]:

# 搞事情
zhangsan._Student__age = -1
zhangsan.show()

 

name:张三,age:-1

 

建议你不要这么干,不同版本的Python解释器可能会把__age改成不同的变量名

有些时候,你会看到以一个下划线开头的实例变量名,比如_age这样的实例变量,外部是可以访问的。

但是,请把它视为私有变量,不要随意访问(Python很多东西全凭自觉~捂脸@_@)

小潘终于长叹一口气,然后还不忘取笑小明同学~你这属性搞的,真麻烦,总是通过方法调用,太累了 <_> 鄙视!

这可把小明急的啊,学习的积极性都没有了,吃了碗牛肉面就去伟哥那边好好取经了~

In [15]:

# 私有方法一笔带过
class Student(object):
    """私有方法"""
    def __go_home(self):
        pass


zhangsan = Student()
zhangsan.__go_home() # 访问不到

 

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-45c76191b808> in <module>()
      7 
      8 zhangsan = Student()
----> 9zhangsan.__go_home() # 访问不到

AttributeError: 'Student' object has no attribute '__go_home'

 

本文由金沙官网线上发布于操作系统,转载请注明出处:Python3 与 C# 面向对象之~封装

您可能还会对下面的文章感兴趣: