2018.6.18 Python第八课

类和对象


1.类和对象
类的定义:具有相同的属性和方法的对象的集合。
对象的定义:在python中,万物皆对象。一个对象的特征也称为属性(attribute)。它所具有的行为也称为方法(method),所以:对象=属性+方法
比如:人类,动物,植物等,这都是一个类。而,某个人,你家的那只狗和门外的那棵树都是一个对象。


2.创建和使用类
创建Dog类,赋予dog蹲下sit()和打滚roll_over()的能力

class Dog():
    def __init__(self,name,age):
        self.name=name;
        self.age=age;
    def sit(self):
        print(self.name.title()+" is now sitting.")
    def roll_over(self):
        print(self.name.title()+" rolled over!")

在Python中,创建一个类的语法为:
class 类名:
属性
方法


3.通过类实例化对象
一个类可以实例化出多个对象,这些对象都具有相同的属性。

class Dog():
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def sit(self):
        print(self.name+" is now sitting.")
    def roll_over(self):
        print(self.name+" rolled over!")
#一个类可以实例化出多个对象
dog1=Dog('Tom',2)
dog2=Dog('Jack',3)
#通过(对象.方法)调用该方法
dog1.sit()
dog2.roll_over()

以上实例输出结果为:
Tom is now sitting.
Jack rolled over!


4.普通字段和静态字段
类里可以有多个属性,也可以有多个方法。同时也有自己的字段。
这里的属性 官方语言叫做 类变量,属于这个类的公用部分,谁都可以来用,所以通常不作为实例变量使用,就是一个符号。而方法,则是在类中定义的函数,可以理解为类的行为部分。

class Province:  
    # 静态字段  
    country='中国'  
    def __init__(self, name):  
        # 普通字段        普通字段在每个对象中都要保存一份  
        self.name = name  
 # 直接访问普通字段  
obj = Province('河北省')  
print(obj.name)  
# 直接访问静态字段        静态字段在内存中只保存一份  
print(Province.country) 

以上实例输出结果为:
河北省
中国


5.@classmethod和@staticmethod
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
@classmethod 表示下面的方法是类方法 将类本身作为对象进行操作的方法。不需要self参数,但第一个参数需要是表示自身类的cls参数。
@staticmethod 标明这是一个静态方法,不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

class Foo:  
    def __init__(self, name):  
        self.name = name    
    def ord_func(self):  
        """ 定义普通方法,至少有一个self参数 """  
        # print(self.name)  
        print('普通方法')

    @classmethod  #表示下面的方法是类方法  将类本身作为对象进行操作的方法  
    def class_func(cls):  
        """ 定义类方法,至少有一个cls参数 """  
        print('类方法') 

    @staticmethod   #标明这是一个静态方法  
    def static_func():  
        """ 定义静态方法 ,无默认参数"""  
        print('静态方法')  
# 调用普通方法  
f = Foo('tom')  
f.ord_func()  
# 调用类方法  
Foo.class_func()  
# 调用静态方法  
Foo.static_func()   

以上实例输出结果为:
普通方法
类方法
静态方法


6.python私有变量与封装
1、 _xx 以单下划线开头的表示的是protected类型的变量。即保护类型只能允许其本身与子类进行访问。若内部变量标示,如: 当使用“from M import”时,不会将以一个下划线开头的对象引入 。
2、 __xx 双下划线的表示的是私有类型的变量。只能允许这个类本身进行访问了,连子类也不可以用于命名一个类属性(类变量),调用时名字被改变(在类FooBar内部,__boo变成_FooBar__boo,如self._FooBar__boo)
3、 __ xx __定义的是特列方法。用户控制的命名空间内的变量或是属性,如__ init __ , __import__或是__file__ 。只有当文档有说明时使用,不要自己定义这类变量。 (就是说这些是python内部定义的变量名)
4、get/set方法:不写init()方法:
基本语法为:
set_name(self,new_name):
self.name=new_name
get_name(self):
return self.name

下面看段代码:

class Gs:
    def set_name(self,name):
        self.__name=name
    def get_name(self):
        return self.__name
    def set_age(self,age):
        if type(age)==int and age>0:
            self.__age=age
        else:
            self.__age=None
            print("年龄只能为纯数字且不能小于0")
    def get_age(self):
        return self.__age
    def set_sex(self,sex):
        if sex=="男" or sex=="女":
            self.__sex=sex
        else:
            self.__sex="{您的性别输入有误}"
            print("您只能输入男或女")
    def get_sex(self):
        return self.__sex
zhangsan=Gs()
zhangsan.set_age(20)
zhangsan.set_sex("nan")
zhangsan.set_name("张三")
print("姓名是{0},年龄为{1},性别是{2}".format(zhangsan.get_name(),zhangsan.get_age(),zhangsan.get_sex()))

以上代码运行结果为:
您只能输入男或女
姓名是张三,年龄为20,性别是{您的性别输入有误}


需要注意的是:
外部不能直接访问私有属性,比如:

class A(object):  
    def __init__(self):  
        self.__data=[]  #翻译成 self._A__data=[]  
    def add(self,item):  
        self.__data.append(item) #翻译成 self._A__data.append(item)  
    def printData(self):  
        print(self.__data)  #翻译成 self._A__data  

a=A()  
a.add('hello')  
a.add('python')  
a.printData()  
#print(a.__data)  #外界不能访问私有变量,此处执行会报错 AttributeError: 'A' object has no attribute '__data'
#如果想直接在外部调用私有属性如下  
print a._A__data #通过这种方式,在外面也能够访问“私有”变量;这一点在调试中是比较有用的!

输出结果为:
['hello', 'python']
['hello', 'python']


7.python中面向对象之魔法方法(__init__,__str__方法,__del__方法,__new__方法)

魔法方法: 以两个_下划线开头 和以两个_下划线结尾的方法
魔法方法是python提供给我们的 由龟叔定义的好的方法 程序员直接使用即可
在特殊的情况下(不同的魔法方法 被调用的条件不同) 被python调用。


7.1__init__() 初始化对象
创建类时,可以定义一个特定的方法,名为init(),只要创建这个类的一个实例
就会运行这个方法。可以向__init__()方法传递参数,
这样创建对象时就可以把属性设置为你希望的值
__init__()这个方法会在创建对象时完成初始化。

class Peo:
     def __init__(self,name,age,sex):
            self.Name = name
            self.Age = age
            self.Sex = sex
     def speak(self):
             print "my name" + self.Name 
#实例化这个类的对象时:
zhangsan=Peo("zhangsan",24,'man')
print(zhangsan.Age)
#输出结果为 24
print(zhangsan.Name)
#输出结果为 zhangsan
print(zhangsan.Sex)
#输出结果为 man

7.2__str__()方法
作用: 是追踪对象属性信息变量 一般用于程序员调试代码
特点: 有且只有一个形参 那就是self
他必须有返回值 返回值类型为字符串
什么时候被python调用: 在当前监听到程序员打印这个类创建的对象的时候.

在我们没有实现str方法的时候 打印对象 会输出十六进制地址

print(zhangsan)

输出结果为:<object at 0x00000247ED3B5080>
如果我们实现了str方法 打印对象 会输出str方法中的字符串

def __str__(self):
    return "名字:%s 性别:%s 年龄:%d" %(zhangsan.Name,zhangsan.Sex,zhangsan.Age)
print(zhangsan)

此时输出结果为:名字:zhangsan 性别:man 年龄:24


7.3__del__()方法
当程序的代码执行完成后 将要结束 那么会执行del huanghzong 杀死对象 系统释放内存
在当前对象的引用计数为0的时候才会执行
当有变量保存一个对象的引用,这个变量的引用计数就会+1
当用del()删除变量指向的对象时,会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会真正被删除(内存被回收)

def __del__(self):
    print("再见!!!")

7.4__new__()方法

class Person(object):
    #一个对象的生成是在new方法中完成的
    def __new__(cls, *args, **kwargs):
        print("__new__")
        #委托父类帮我们创建一个对象 然后子类返回对象
        return object.__new__(cls)
    '''
    构造方法(构造函数)
    调用的时间: 当使用这个类创建一个对象成功 就会调用
    作用: 给这个对象添加属性并赋值
    '''
    def __init__(self):
        print("__init__")
        self.name = "小明"

    #追踪对象属性信息变化
    def __str__(self):
        return "名字:%s" % self.name

    #监听对象销毁后执行del方法
    def __del__(self):
        print("再见")

xiaoming = Person()
print(xiaoming)

实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法
__new__()必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。
__init__()有一个参数self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。
若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。
我们可以将类比作制造商,__new__()方法就是前期的原材料购买环节,__init__()方法就是在有原材料的基础上,加工,初始化商品环节。
实际应用过程中,我们可以这么使用:

class LxmlDocument(object_ref):
      cache = weakref.WeakKeyDictionary()
      __slots__ = ['__weakref__']

      def __new__(cls, response, parser=etree.HTMLParser):
            cache = cls.cache.setdefault(response, {})
            if parser not in cache:
                  obj = object_ref.__new__(cls)
                  cache[parser] = _factory(response, parser)
            return cache[parser]

该类中的new()方法的使用,就是再进行初始化之前,检查缓存中是否存在该对象,如果存在则将缓存存放对象直接返回,如果不存在,则将对象放至缓存中,供下次使用。

也可以实现单例模式:

class SingleDemo:
    __sd=False
    def __new__(cls):
        if cls.__sd==False:
            cls.__sd=object.__new__(cls)
        return cls.__sd
a=SingleDemo()
b=SingleDemo()
print(a,b)

此时会发现输出结果对象a和b都指向了同一个地址。

猜你喜欢

转载自blog.csdn.net/weixin_40313627/article/details/80739951