元類理解與元類編程 《Python3網絡爬蟲開發》中第九章代理的使用代碼Crawler中代碼的理解

__new__與__init__的理解

  __new__()方法是在建立實例以前被調用的,它的做用是建立一個實例,而後返回該實例對象,它是一個靜態方法。html

  __init__() 當實例被建立完成以後被調用的,而後設置對象屬性的一些初始值,是一個實例方法。python

也即:__new__先被調用,__init__後被調用,__new__方法中的返回值將實例傳遞給__init__方法中的第一個參數。而後__init__給這個實例設置一些初始參數。web

注意:json

一、繼承自object的新式類纔有__new__網絡

二、__new__至少要有一個參數cls,表明當前類,此參數在實例化時由Python解釋器自動識別session

三、__new__必需要有返回值,返回實例化出來的實例,這點在本身實現__new__時要特別注意,能夠return父類(經過super(當前類名, cls))__new__出來的實例,或者直接是object的__new__出來的實例app

四、__init__有一個參數self,就是這個__new__返回的實例,__init__在__new__的基礎上能夠完成一些其它初始化的動做,__init__不須要返回值函數

五、若是__new__建立的是當前類的實例,會自動調用__init__函數,經過return語句裏面調用的__new__函數的第一個參數是 cls 來保證是當前類實例,若是是其餘類的類名;那麼實際建立返回的就是其餘類的實例,其實就不會調用當前類的__init__函數,也不會調用其餘類的__init__函數。url

六、在定義子類時沒有從新定義__new__()時,Python默認是調用該類的直接父類的__new__()方法來構造該類的實例,若是該類的父類也沒有重寫__new__(),那麼將一直按此規矩追溯至object的__new__()方法,由於object是全部新式類的基類。spa

十、將類比做製造商,__new__方法就是前期的原材料購買環節,__init__方法就是在有原材料的基礎上,加工,初始化商品環節

十一、__new__的通常寫法:

  

複製代碼
class A(object): # -> don t forget the object specified as base

  def __new__(cls):
    print ("A.__new__ called")
    return super(A, cls).__new__(cls)
  #return super().__new__(cls) # 這樣子寫也能夠,注意要與類的繼承簡寫區別一下,後面的cls不能夠省略。
def __init__(self): print("A.__init__ called") A()
複製代碼

經過元類建立類

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import json
import re from .utils import get_page from pyquery import PyQuery as pq class ProxyMetaclass(type): def __new__(cls, name, bases, attrs): count = 0 attrs['__CrawlFunc__'] = [] for k, v in attrs.items(): if 'crawl_' in k: attrs['__CrawlFunc__'].append(k) count += 1 attrs['__CrawlFuncCount__'] = count return type.__new__(cls, name, bases, attrs) class Crawler(object, metaclass=ProxyMetaclass): def get_proxies(self, callback): proxies = [] for proxy in eval("self.{}()".format(callback)): print('成功獲取到代理', proxy) proxies.append(proxy) return proxies def crawl_daili66(self, page_count=4): """ 獲取代理66 :param page_count: 頁碼 :return: 代理 """ start_url = 'http://www.66ip.cn/{}.html' urls = [start_url.format(page) for page in range(1, page_count + 1)] for url in urls: print('Crawling', url) html = get_page(url) if html: doc = pq(html) trs = doc('.containerbox table tr:gt(0)').items() for tr in trs: ip = tr.find('td:nth-child(1)').text() port = tr.find('td:nth-child(2)').text() yield ':'.join([ip, port]) def crawl_kuaidaili(self): for i in range(1, 4): start_url = 'http://www.kuaidaili.com/free/inha/{}/'.format(i) html = get_page(start_url) if html: ip_address = re.compile('<td data-title="IP">(.*?)</td>') re_ip_address = ip_address.findall(html) port = re.compile('<td data-title="PORT">(.*?)</td>') re_port = port.findall(html) for address, port in zip(re_ip_address, re_port): address_port = address + ':' + port yield address_port.replace(' ', '') def crawl_xicidaili(self): for i in range(1, 3): start_url = 'http://www.xicidaili.com/nn/{}'.format(i) headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Cookie': '_free_proxy_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWRjYzc5MmM1MTBiMDMzYTUzNTZjNzA4NjBhNWRjZjliBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMUp6S2tXT3g5a0FCT01ndzlmWWZqRVJNek1WanRuUDBCbTJUN21GMTBKd3M9BjsARg%3D%3D--2a69429cb2115c6a0cc9a86e0ebe2800c0d471b3', 'Host': 'www.xicidaili.com', 'Referer': 'http://www.xicidaili.com/nn/3', 'Upgrade-Insecure-Requests': '1', } html = get_page(start_url, options=headers) if html: find_trs = re.compile('<tr class.*?>(.*?)</tr>', re.S) trs = find_trs.findall(html) for tr in trs: find_ip = re.compile('<td>(\d+\.\d+\.\d+\.\d+)</td>') re_ip_address = find_ip.findall(tr) find_port = re.compile('<td>(\d+)</td>') re_port = find_port.findall(tr) for address, port in zip(re_ip_address, re_port): address_port = address + ':' + port yield address_port.replace(' ', '')

 

根據網絡資料以及我的理解,此段代碼用到元類的理解以下:

  這裏藉助了元類來實現全部ProxyMetaclass類中以crawl__開頭的方法,具體實現過程以下:

1.首先定義ProxyMetaclass類做爲Crawler類的元類,元類中實現__new__()方法,這個方法有固定的幾個參數,其中參數含義以下:

  name:當前類的名字。

  bases:當前類繼承的父類,以元組形式提交。

  attrs:當前類中全部的屬性(包括方法也能夠看做特殊的屬性)

2.遍歷attrs參數便可獲取類的全部方法信息,而後判斷方法是否以crawl__開頭,若是是則將其添加到__CrawlFunc__屬性中。這樣就將全部的以crawl__開頭的方法定義成了一個屬性,動態獲取到全部以crawl__開頭的方法。

 過程理解:

  當Crawler中啓用元類(即實現metaclass=ProxyMetaclass)時,意味着Crawler中的__new__()方法繼承自ProxyMetaclass的__new__()方法,該方法的第一個參數cls就表明建立的Crawler類的實例,name爲其名字,bases爲其父類方法,而後attrs爲其全部類方法(其中的key即方法名,value爲方法返回值),最後metaclass又經過returnreturn type.__new__(cls, name, bases, attrs)方法,將構建實例的工做又交給了type去完成。只是在傳遞參數attrs中作了一些改變,因而前面過程2中的方法實現以後使得建立的Crawler類的實例具備了屬性__CrawlFunc__ = [crawl_daili66, crawl_kuaidaili, , crawl_xicidaili,  crawl_ip3366, crawl_iphai, crawl_data5u] 和屬性__CrawlFuncCount__ = 6。實現了動態地設計類。

相關文章
相關標籤/搜索