Are PDO prepared statements sufficient to prevent SQL injection?

Questions : Are PDO prepared statements sufficient to prevent SQL injection?

Let’s say I have code like this:

$dbh = new PDO("blahblah"); $stmt = $dbh->prepare('SELECT * FROM users where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

The PDO documentation says:

The parameters to prepared statements don’t need to be quoted; the driver handles it for you.

Is that truly all I need to do to avoid SQL injections? Is it really that easy?

You can assume MySQL if it makes a difference. Also, I’m really only curious about the use of prepared statements against SQL injection. In this context, I don’t care about XSS or other possible vulnerabilities.

Total Answers: 7 Answers 7


Popular Answers:

  1. The short answer is NO, PDO prepares will not defend you from all possible SQL-Injection attacks. For certain obscure edge-cases.

    I’m adapting this answer to talk about PDO…

    The long answer isn’t so easy. It’s based off an attack demonstrated here.

    The Attack

    So, let’s start off by showing the attack…

    $pdo->query('SET NAMES gbk'); $var = "xbfx27 OR 1=1 /*"; $query = 'SELECT * FROM test WHERE name = ? LIMIT 1'; $stmt = $pdo->prepare($query); $stmt->execute(array($var)); 

    In certain circumstances, that will return more than 1 row. Let’s dissect what’s going on here:

    1. Selecting a Character Set

      $pdo->query('SET NAMES gbk'); 

      For this attack to work, we need the encoding that the server’s expecting on the connection both to encode ' as in ASCII i.e. 0x27 and to have some character whose final byte is an ASCII i.e. 0x5c. As it turns out, there are 5 such encodings supported in MySQL 5.6 by default: big5, cp932, gb2312, gbk and sjis. We’ll select gbk here.

      Now, it’s very important to note the use of SET NAMES here. This sets the character set ON THE SERVER. There is another way of doing it, but we’ll get there soon enough.

    2. The Payload

      The payload we’re going to use for this injection starts with the byte sequence 0xbf27. In gbk, that’s an invalid multibyte character; in latin1, it’s the string ¿'. Note that in latin1 and gbk, 0x27 on its own is a literal ' character.

      We have chosen this payload because, if we called addslashes() on it, we’d insert an ASCII i.e. 0x5c, before the ' character. So we’d wind up with 0xbf5c27, which in gbk is a two character sequence: 0xbf5c followed by 0x27. Or in other words, a valid character followed by an unescaped '. But we’re not using addslashes(). So on to the next step…

    3. $stmt->execute()

      The important thing to realize here is that PDO by default does NOT do true prepared statements. It emulates them (for MySQL). Therefore, PDO internally builds the query string, calling mysql_real_escape_string() (the MySQL C API function) on each bound string value.

      The C API call to mysql_real_escape_string() differs from addslashes() in that it knows the connection character set. So it can perform the escaping properly for the character set that the server is expecting. However, up to this point, the client thinks that we’re still using latin1 for the connection, because we never told it otherwise. We did tell the server we’re using gbk, but the client still thinks it’s latin1.

      Therefore the call to mysql_real_escape_string() inserts the backslash, and we have a free hanging ' character in our “escaped” content! In fact, if we were to look at $var in the gbk character set, we’d see:

      縗' OR 1=1 /*

      Which is exactly what the attack requires.

    4. The Query

      This part is just a formality, but here’s the rendered query:

      SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1 

    Congratulations, you just successfully attacked a program using PDO Prepared Statements…

    The Simple Fix

    Now, it’s worth noting that you can prevent this by disabling emulated prepared statements:

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

    This will usually result in a true prepared statement (i.e. the data being sent over in a separate packet from the query). However, be aware that PDO will silently fallback to emulating statements that MySQL can’t prepare natively: those that it can are listed in the manual, but beware to select the appropriate server version).

    The Correct Fix

    The problem here is that we didn’t call the C API’s mysql_set_charset() instead of SET NAMES. If we did, we’d be fine provided we are using a MySQL release since 2006.

    If you’re using an earlier MySQL release, then a bug in mysql_real_escape_string() meant that invalid multibyte characters such as those in our payload were treated as single bytes for escaping purposes even if the client had been correctly informed of the connection encoding and so this attack would still succeed. The bug was fixed in MySQL 4.1.20, 5.0.22 and 5.1.11.

    But the worst part is that PDO didn’t expose the C API for mysql_set_charset() until 5.3.6, so in prior versions it cannot prevent this attack for every possible command! It’s now exposed as a DSN parameter, which should be used instead of SET NAMES

    The Saving Grace

    As we said at the outset, for this attack to work the database connection must be encoded using a vulnerable character set. utf8mb4 is not vulnerable and yet can support every Unicode character: so you could elect to use that instead—but it has only been available since MySQL 5.5.3. An alternative is utf8, which is also not vulnerable and can support the whole of the Unicode Basic Multilingual Plane.

    Alternatively, you can enable the NO_BACKSLASH_ESCAPES SQL mode, which (amongst other things) alters the operation of mysql_real_escape_string(). With this mode enabled, 0x27 will be replaced with 0x2727 rather than 0x5c27 and thus the escaping process cannot create valid characters in any of the vulnerable encodings where they did not exist previously (i.e. 0xbf27 is still 0xbf27 etc.)—so the server will still reject the string as invalid. However, see @eggyal’s answer for a different vulnerability that can arise from using this SQL mode (albeit not with PDO).

    Safe Examples

    The following examples are safe:

    mysql_query('SET NAMES utf8'); $var = mysql_real_escape_string("xbfx27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1"); 

    Because the server’s expecting utf8

    mysql_set_charset('gbk'); $var = mysql_real_escape_string("xbfx27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1"); 

    Because we’ve properly set the character set so the client and the server match.

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->query('SET NAMES gbk'); $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(array("xbfx27 OR 1=1 /*")); 

    Because we’ve turned off emulated prepared statements.

    $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password); $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(array("xbfx27 OR 1=1 /*")); 

    Because we’ve set the character set properly.

    $mysqli->query('SET NAMES gbk'); $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $param = "xbfx27 OR 1=1 /*"; $stmt->bind_param('s', $param); $stmt->execute(); 

    Because MySQLi does true prepared statements all the time.

    Wrapping Up

    If you:

    • Use Modern Versions of MySQL (late 5.1, all 5.5, 5.6, etc) AND PDO’s DSN charset parameter (in PHP ≥ 5.3.6)

    OR

    • Don’t use a vulnerable character set for connection encoding (you only use utf8 / latin1 / ascii / etc)

    OR

    • Enable NO_BACKSLASH_ESCAPES SQL mode

    You’re 100% safe.

    Otherwise, you’re vulnerable even though you’re using PDO Prepared Statements…

    Addendum

    I’ve been slowly working on a patch to change the default to not emulate prepares for a future version of PHP. The problem that I’m running into is that a LOT of tests break when I do that. One problem is that emulated prepares will only throw syntax errors on execute, but true prepares will throw errors on prepare. So that can cause issues (and is part of the reason tests are borking).

  2. No, they are not always.

    It depends on whether you allow user input to be placed within the query itself. For example:

    $dbh = new PDO("blahblah"); $tableToUse = $_GET['userTable']; $stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

    would be vulnerable to SQL injections and using prepared statements in this example won’t work, because the user input is used as an identifier, not as data. The right answer here would be to use some sort of filtering/validation like:

    $dbh = new PDO("blahblah"); $tableToUse = $_GET['userTable']; $allowedTables = array('users','admins','moderators'); if (!in_array($tableToUse,$allowedTables)) $tableToUse = 'users'; $stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

    Note: you can’t use PDO to bind data that goes outside of DDL (Data Definition Language), i.e. this does not work:

    $stmt = $dbh->prepare('SELECT * FROM foo ORDER BY :userSuppliedData'); 

    The reason why the above does not work is because DESC and ASC are not data. PDO can only escape for data. Secondly, you can’t even put ' quotes around it. The only way to allow user chosen sorting is to manually filter and check that it’s either DESC or ASC.

  3. Yes, it is sufficient. The way injection type attacks work, is by somehow getting an interpreter (The database) to evaluate something, that should have been data, as if it was code. This is only possible if you mix code and data in the same medium (Eg. when you construct a query as a string).

    Parameterised queries work by sending the code and the data separately, so it would never be possible to find a hole in that.

    You can still be vulnerable to other injection-type attacks though. For example, if you use the data in a HTML-page, you could be subject to XSS type attacks.

  4. Personally I would always run some form of sanitation on the data first as you can never trust user input, however when using placeholders / parameter binding the inputted data is sent to the server separately to the sql statement and then binded together. The key here is that this binds the provided data to a specific type and a specific use and eliminates any opportunity to change the logic of the SQL statement.

  5. Eaven if you are going to prevent sql injection front-end, using html or js checks, you’d have to consider that front-end checks are “bypassable”.

    You can disable js or edit a pattern with a front-end development tool (built in with firefox or chrome nowadays).

    So, in order to prevent SQL injection, would be right to sanitize input date backend inside your controller.

    I would like to suggest to you to use filter_input() native PHP function in order to sanitize GET and INPUT values.

    If you want to go ahead with security, for sensible database queries, I’d like to suggest to you to use regular expression to validate data format. preg_match() will help you in this case! But take care! Regex engine is not so light. Use it only if necessary, otherwise your application performances will decrease.

    Security has a costs, but do not waste your performance!

    Easy example:

    if you want to double check if a value, received from GET is a number, less then 99 if(!preg_match(‘/[0-9]{1,2}/’)){…} is heavyer of

    if (isset($value) && intval($value)) <99) {...} 

    So, the final answer is: “No! PDO Prepared Statements does not prevent all kind of sql injection”; It does not prevent unexpected values, just unexpected concatenation

  6. Yet another TL;DR

    Iterator on list: next() returns the next element of the list

    Iterator generator: next() will compute the next element on the fly (execute code)

    You can see the yield/generator as a way to manually run the control flow from outside (like continue loop one step), by calling next, however complex the flow.

    Note: The generator is NOT a normal function. It remembers the previous state like local variables (stack). See other answers or articles for detailed explanation. The generator can only be iterated on once. You could do without yield, but it would not be as nice, so it can be considered ‘very nice’ language sugar.

  7. Here’s a simple yield based approach, to compute the fibonacci series, explained:

    def fib(limit=50): a, b = 0, 1 for i in range(limit): yield b a, b = b, a+b 

    When you enter this into your REPL and then try and call it, you’ll get a mystifying result:

    >>> fib() <generator object fib at 0x7fa38394e3b8> 

    This is because the presence of yield signaled to Python that you want to create a generator, that is, an object that generates values on demand.

    So, how do you generate these values? This can either be done directly by using the built-in function next, or, indirectly by feeding it to a construct that consumes values.

    Using the built-in next() function, you directly invoke .next/__next__, forcing the generator to produce a value:

    >>> g = fib() >>> next(g) 1 >>> next(g) 1 >>> next(g) 2 >>> next(g) 3 >>> next(g) 5 

    Indirectly, if you provide fib to a for loop, a list initializer, a tuple initializer, or anything else that expects an object that generates/produces values, you’ll “consume” the generator until no more values can be produced by it (and it returns):

    results = [] for i in fib(30): # consumes fib results.append(i) # can also be accomplished with results = list(fib(30)) # consumes fib 

    Similarly, with a tuple initializer:

    >>> tuple(fib(5)) # consumes fib (1, 1, 2, 3, 5) 

    A generator differs from a function in the sense that it is lazy. It accomplishes this by maintaining it’s local state and allowing you to resume whenever you need to.

    When you first invoke fib by calling it:

    f = fib() 

    Python compiles the function, encounters the yield keyword and simply returns a generator object back at you. Not very helpful it seems.

    When you then request it generates the first value, directly or indirectly, it executes all statements that it finds, until it encounters a yield, it then yields back the value you supplied to yield and pauses. For an example that better demonstrates this, let’s use some print calls (replace with print "text" if on Python 2):

    def yielder(value): """ This is an infinite generator. Only use next on it """ while 1: print("I'm going to generate the value for you") print("Then I'll pause for a while") yield value print("Let's go through it again.") 

    Now, enter in the REPL:

    >>> gen = yielder("Hello, yield!") 

    you have a generator object now waiting for a command for it to generate a value. Use next and see what get’s printed:

    >>> next(gen) # runs until it finds a yield I'm going to generate the value for you Then I'll pause for a while 'Hello, yield!' 

    The unquoted results are what’s printed. The quoted result is what is returned from yield. Call next again now:

    >>> next(gen) # continues from yield and runs again Let's go through it again. I'm going to generate the value for you Then I'll pause for a while 'Hello, yield!' 

    The generator remembers it was paused at yield value and resumes from there. The next message is printed and the search for the yield statement to pause at it performed again (due to the while loop).

  8. yield is similar to return. The difference is:

    yield makes a function iterable (in the following example primes(n = 1) function becomes iterable).
    What it essentially means is the next time the function is called, it will continue from where it left (which is after the line of yield expression).

    def isprime(n): if n == 1: return False for x in range(2, n): if n % x == 0: return False else: return True def primes(n = 1): while(True): if isprime(n): yield n n += 1 for n in primes(): if n > 100: break print(n) 

    In the above example if isprime(n) is true it will return the prime number. In the next iteration it will continue from the next line

    n += 1 
  9. In Python generators (a special type of iterators) are used to generate series of values and yield keyword is just like the return keyword of generator functions.

    The other fascinating thing yield keyword does is saving the state of a generator function.

    So, we can set a number to a different value each time the generator yields.

    Here’s an instance:

    def getPrimes(number): while True: if isPrime(number): number = yield number # a miracle occurs here number += 1 def printSuccessivePrimes(iterations, base=10): primeGenerator = getPrimes(base) primeGenerator.send(None) for power in range(iterations): print(primeGenerator.send(base ** power)) 
  10. In [4]: def make_cake(numbers): ...: for i in range(numbers): ...: yield 'Cake {}'.format(i) ...: In [5]: factory = make_cake(5)
  11. All of the answers here are great; but only one of them (the most voted one) relates to how your code works. Others are relating to generators in general, and how they work.

    So I won’t repeat what generators are or what yields do; I think these are covered by great existing answers. However, after spending few hours trying to understand a similar code to yours, I’ll break it down how it works.

    Your code traverse a binary tree structure. Let’s take this tree for example:

     5 /  3 6 /   1 4 8 

    And another simpler implementation of a binary-search tree traversal:

    class Node(object): .. def __iter__(self): if self.has_left_child(): for child in self.left: yield child yield self.val if self.has_right_child(): for child in self.right: yield child 

    The execution code is on the Tree object, which implements __iter__ as this:

    def __iter__(self): class EmptyIter(): def next(self): raise StopIteration if self.root: return self.root.__iter__() return EmptyIter() 

    The while candidates statement can be replaced with for element in tree; Python translate this to

    it = iter(TreeObj) # returns iter(self.root) which calls self.root.__iter__() for element in it: .. process element .. 

    Because Node.__iter__ function is a generator, the code inside it is executed per iteration. So the execution would look like this:

    1. root element is first; check if it has left childs and for iterate them (let’s call it it1 because its the first iterator object)
    2. it has a child so the for is executed. The for child in self.left creates a new iterator from self.left, which is a Node object itself (it2)
    3. Same logic as 2, and a new iterator is created (it3)
    4. Now we reached the left end of the tree. it3 has no left childs so it continues and yield self.value
    5. On the next call to next(it3) it raises StopIteration and exists since it has no right childs (it reaches to the end of the function without yield anything)
    6. it1 and it2 are still active – they are not exhausted and calling next(it2) would yield values, not raise StopIteration
    7. Now we are back to it2 context, and call next(it2) which continues where it stopped: right after the yield child statement. Since it has no more left childs it continues and yields it’s self.val.

    The catch here is that every iteration creates sub-iterators to traverse the tree, and holds the state of the current iterator. Once it reaches the end it traverse back the stack, and values are returned in the correct order (smallest yields value first).

    Your code example did something similar in a different technique: it populated a one-element list for every child, then on the next iteration it pops it and run the function code on the current object (hence the self).

    I hope this contributed a little to this legendary topic. I spent several good hours drawing this process to understand it.

  12. The yield keyword in Python used to exit from the code without disturbing the state of local variables and when again the function is called the execution starts from the last point where we left the code.

    The below example demonstrates the working of yield:

    def counter(): x=2 while x < 5: yield x x += 1 print("Initial value of x: ", counter()) for y in counter(): print(y) 

    The above code generates the Below output:

    Initial value of x: <generator object counter at 0x7f0263020ac0> 2 3 4 
  13. Can also send data back to the generator!

    Indeed, as many answers here explain, using yield creates a generator.

    You can use the yield keyword to send data back to a “live” generator.

    Example:

    Let’s say we have a method which translates from english to some other language. And in the beginning of it, it does something which is heavy and should be done once. We want this method run forever (don’t really know why.. :)), and receive words words to be translated.

    def translator(): # load all the words in English language and the translation to 'other lang' my_words_dict = {'hello': 'hello in other language', 'dog': 'dog in other language'} while True: word = (yield) yield my_words_dict.get(word, 'Unknown word...') 

    Running:

    my_words_translator = translator() next(my_words_translator) print(my_words_translator.send('dog')) next(my_words_translator) print(my_words_translator.send('cat')) 

    will print:

    dog in other language Unknown word... 

    To summarise:

    use send method inside a generator to send data back to the generator. To allow that, a (yield) is used.

  14. yield in python is in a way similar to the return statement, except for some differences. If multiple values have to be returned from a function, return statement will return all the values as a list and it has to be stored in the memory in the caller block. But what if we don’t want to use extra memory? Instead, we want to get the value from the function when we need it. This is where yield comes in. Consider the following function :-

    def fun(): yield 1 yield 2 yield 3 

    And the caller is :-

    def caller(): print ('First value printing') print (fun()) print ('Second value printing') print (fun()) print ('Third value printing') print (fun()) 

    The above code segment (caller function) when called, outputs :-

    First value printing 1 Second value printing 2 Third value printing 3 

    As can be seen from above, yield returns a value to its caller, but when the function is called again, it doesn’t start from the first statement, but from the statement right after the yield. In the above example, “First value printing” was printed and the function was called. 1 was returned and printed. Then “Second value printing” was printed and again fun() was called. Instead of printing 1 (the first statement), it returned 2, i.e., the statement just after yield 1. The same process is repeated further.

  15. Simple answer

    When function contains at least one yield statement, the function automaticly becomes generator function. When you call generator function, python executes code in the generator function until yield statement occur. yield statement freezes the function with all its internal states. When you call generator function again, python continues execution of code in the generator function from frozen position, until yield statement occur again and again. The generator function executes code until generator function runs out without yield statement.

    Benchmark

    Create a list and return it:

    def my_range(n): my_list = [] i = 0 while i < n: my_list.append(i) i += 1 return my_list @profile def function(): my_sum = 0 my_values = my_range(1000000) for my_value in my_values: my_sum += my_value function() 

    Results with:

    Total time: 1.07901 s Timer unit: 1e-06 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 9 @profile 10 def function(): 11 1 1.1 1.1 0.0 my_sum = 0 12 1 494875.0 494875.0 45.9 my_values = my_range(1000000) 13 1000001 262842.1 0.3 24.4 for my_value in my_values: 14 1000000 321289.8 0.3 29.8 my_sum += my_value Line # Mem usage Increment Occurences Line Contents ============================================================ 9 40.168 MiB 40.168 MiB 1 @profile 10 def function(): 11 40.168 MiB 0.000 MiB 1 my_sum = 0 12 78.914 MiB 38.746 MiB 1 my_values = my_range(1000000) 13 78.941 MiB 0.012 MiB 1000001 for my_value in my_values: 14 78.941 MiB 0.016 MiB 1000000 my_sum += my_value 

    Generate values on the fly:

    def my_range(n): i = 0 while i < n: yield i i += 1 @profile def function(): my_sum = 0 for my_value in my_range(1000000): my_sum += my_value function() 

    Results with:

    Total time: 1.24841 s Timer unit: 1e-06 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 7 @profile 8 def function(): 9 1 1.1 1.1 0.0 my_sum = 0 10 11 1000001 895617.3 0.9 71.7 for my_value in my_range(1000000): 12 1000000 352793.7 0.4 28.3 my_sum += my_value Line # Mem usage Increment Occurences Line Contents ============================================================ 7 40.168 MiB 40.168 MiB 1 @profile 8 def function(): 9 40.168 MiB 0.000 MiB 1 my_sum = 0 10 11 40.203 MiB 0.016 MiB 1000001 for my_value in my_range(1000000): 12 40.203 MiB 0.020 MiB 1000000 my_sum += my_value 

    Summary

    The generator function needs a little more time to execute, than function which returns a list but it use much less memory.

  16. A simple use case:

    >>> def foo(): yield 100 yield 20 yield 3 >>> for i in foo(): print(i) 100 20 3 >>>  

    How it works: when called, the function returns an object immediately. The object can be passed to the next() function. Whenever the next() function is called, your function runs up until the next yield and provides the return value for the next() function.

    Under the hood, the for loop recognizes that the object is a generator object and uses next() to get the next value.

    In some languages like ES6 and higher, it’s implemented a little differently so next is a member function of the generator object, and you could pass values from the caller every time it gets the next value. So if result is the generator then you could do something like y = result.next(555), and the program yielding values could say something like z = yield 999. The value of y would be 999 that next gets from the yield, and the value of z would be 555 that yield gets from the next. Python get and send methods have a similar effect.

  17. Usually, it’s used to create an iterator out of function. Think ‘yield’ as an append() to your function and your function as an array. And if certain criteria meet, you can add that value in your function to make it an iterator.

    arr=[] if 2>0: arr.append(2) def func(): if 2>0: yield 2 

    the output will be the same for both.

    The main advantage of using yield is to creating iterators. Iterators don’t compute the value of each item when instantiated. They only compute it when you ask for it. This is known as lazy evaluation.

  18. Generators allow to get individual processed items immediately (without the need to wait for the whole collection to be processed). This is illustrated in the example below.

    import time def get_gen(): for i in range(10): yield i time.sleep(1) def get_list(): ret = [] for i in range(10): ret.append(i) time.sleep(1) return ret start_time = time.time() print('get_gen iteration (individual results come immediately)') for i in get_gen(): print(f'result arrived after: {time.time() - start_time:.0f} seconds') print() start_time = time.time() print('get_list iteration (results come all at once)') for i in get_list(): print(f'result arrived after: {time.time() - start_time:.0f} seconds') 
    get_gen iteration (individual results come immediately) result arrived after: 0 seconds result arrived after: 1 seconds result arrived after: 2 seconds result arrived after: 3 seconds result arrived after: 4 seconds result arrived after: 5 seconds result arrived after: 6 seconds result arrived after: 7 seconds result arrived after: 8 seconds result arrived after: 9 seconds get_list iteration (results come all at once) result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds result arrived after: 10 seconds 
  19. The yield keyword is used in enumeration/iteration where the function is expected to return more then one output. I want to quote this very simple example A:

    # example A def getNumber(): for r in range(1,10): return r 

    The above function will return only 1 even when it’s called multiple times. Now if we replace return with yield as in example B:

    # example B def getNumber(): for r in range(1,10): yield r 

    It will return 1 when first called 2 when called again then 3,4 and it goes to increment till 10.

    Although the example B is conceptually true but to call it in python 3 we have to do the following:

     g = getNumber() #instance print(next(g)) #will print 1 print(next(g)) #will print 2 print(next(g)) #will print 3 # so to assign it to a variables v = getNumber() v1 = next(v) #v1 will have 1 v2 = next(v) #v2 will have 2 v3 = next(v) #v3 will have 3 
  20. names = ['Sam', 'Sarah', 'Thomas', 'James'] # Using function def greet(name) : return f'Hi, my name is {name}.' for each_name in names: print(greet(each_name)) # Output: >>>Hi, my name is Sam. >>>Hi, my name is Sarah. >>>Hi, my name is Thomas. >>>Hi, my name is James. # using generator def greetings(names) : for each_name in names: yield f'Hi, my name is {each_name}.' for greet_name in greetings(names): print (greet_name) # Output: >>>Hi, my name is Sam. >>>Hi, my name is Sarah. >>>Hi, my name is Thomas. >>>Hi, my name is James.
  21. Key points

    • The grammar for Python uses the presence of the yield keyword to make a function that returns a generator.

    • A generator is a kind of iterator, which is that main way that looping occurs in Python.

    • A generator is essentially a resumable function. Unlike return that returns a value and ends a function, the yield keyword returns a value and suspends a function.

    • When next(g) is called on a generator, the function resumes execution where it left off.

    • Only when the function encounters an explicit or implied return does it actually end.

    Technique for writing and understanding generators

    An easy way to understand and think about generators is to write a regular function with print() instead of yield:

    def f(n): for x in range(n): print(x) print(x * 10) 

    Watch what it outputs:

    >>> f(3) 0 0 1 10 2 2 

    When that function is understood, substitute the yield for print to get a generator that produces the same values:

    def f(n): for x in range(n): yield x yield x * 10 

    Which gives:

    >>> list(f(3)) [0, 0, 1, 10, 2, 20] 

    Iterator protocol

    The answer to “what yield does” can be short and simple, but it is part of a larger world, the so-called “iterator protocol”.

    On the sender side of iterator protocol, there are two relevant kinds of objects. The iterables are things you can loop over. And the iterators are objects that track the loop state.

    On the consumer side of the iterator protocol, we call iter() on the iterable object to get a iterator. Then we call next() on the iterator to retrieve values from the iterator. When there is no more data, a StopIteration exception is raised:

    >>> s = [10, 20, 30] # The list is the "iterable" >>> it = iter(s) # This is the "iterator" >>> next(it) # Gets values out of an iterator 10 >>> next(it) 20 >>> next(it) 30 >>> next(it) Traceback (most recent call last): ... StopIteration 

    To make this all easier for us, for-loops call iter and next on our behalf:

    >>> for x in s: ...  print(x) ...  10 20 30 

    A person could write a book about all this, but these are the key points. When I teach Python courses, I’ve found that this is a minimal sufficient explanation to build understand and start using it right away. In particular, the trick of writing a function with print, testing it, and then converting to yield seems to work well with all levels of Python programmers.

  22. To understand its yield function, one must understand what a generator is. Moreover, before understanding generators, you must understand iterables. Iterable: iterable To create a list, you naturally need to be able to read each element one by one. The process of reading its items one by one is called iteration:

    >>> mylist = [1, 2, 3] >>> for i in mylist: ...  print(i) 1 2 3 

    mylist is an iterable. When you use list comprehensions, you create a list and therefore iterable:

    >>> mylist = [x*x for x in range(3)] >>> for i in mylist: ...  print(i) 0 1 4 

    All data structures that can be used for… in… are iterable; lists, strings, files…

    These iterable methods are convenient because you can read them at will, but you store all the values ​​in memory, which is not always desirable when you have many values. Generator: generator A generator is also a kind of iterator, a special kind of iteration, which can only be iterated once. The generator does not store all values ​​in memory, but generates values ​​on the fly:

    generator: generator, generator, generator generates electricity but does not store energy;)

    >>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ...  print(i) 0 1 4 

    As long as () is used instead of [], the list comprehension becomes the generator comprehension. However, since the generator can only be used once, you cannot execute for i in mygenerator a second time: the generator calculates 0, then discards it, then calculates 1, and the last time it calculates 4. The typical black blind man breaks corn.

    The yield keyword is used in the same way as return, except that the function will return the generator.

    >>> def createGenerator(): ...  mylist = range(3) ...  for i in mylist: ...  yield i*i ... >>> mygenerator = createGenerator() >>> print(mygenerator) <generator object createGenerator at 0xb7555c34> >>> for i in mygenerator: ...  print(i) 0 1 4 

    This example itself is useless, but when you need a function to return a large number of values ​​and only need to read it once, using yield becomes convenient.

    To master the yield, one need to be clear is that when a function is called, the code written in the function body will not run. The function only returns the generator object. Beginners are likely to be confused about this.

    Second, understand that the code will continue from where it left off every time for uses the generator.

    The most difficult part now is:

    The first time for calls the generator object created from your function, it will run the code in the function from the beginning until it hits yield, and then it will return the first value of the loop. Then, each subsequent call will run the next iteration of the loop you wrote in the function and return the next value. This will continue until the generator is considered empty, which yields when there is no hit while the function is running. That may be because the loop has ended, or because you are no longer satisfied with “if/else”.

    Personal understanding I hope to help you!

Tasg: php, security

Answer Link
jidam
  • Unable to run NoraUI mvn verify goal
  • Unable to run my app on emulator in VS Code
  • Unable to run multiple instances of libVLC(MobileVLCKit) in IOS via flutter framework
  • Unable to run make on griddb source on ubuntu 20.04 (building from source)
  • Unable to run latexindent macOS Monterey 12.0.1
  • Unable to run kotlinc-native command
  • Unable to run JUnit Test… Java.lang.ExceptionInInitializerError (Android Studio)
  • Unable to run java with -Xmx > 966m
  • Unable to run ionic cap run android from wsl2 inorder to start android emulator
  • Unable to run Intel HAXM installer: Cannot start process, the working directory does not exist
  • fs
  • Unable to run Google Analytics sample code
  • unable to run flutter run after upgarding to flutter 2.8.0 from 2.5.3
  • Unable to run Django with PostgreSQL in Docker
  • Unable to Run Container Using testcontainers
  • Unable to run ClojureScript Hello World program, Error building classpath. Error reading edn.
  • unable to run client command for apache karaf 4.3.3 through remote server
  • Unable to run c program 2nd time using eclipse
  • unable to run c++ in visual studio code on m1 chipset
  • Unable to run Android Instrumented Tests
  • Unable to run adb, check your Android SDK installation and ANDROID_SDK_ROOT environment variable: …AndroidSdkplatform-toolsadb.exe
  • Unable to run a singlespecific .spec.ts file through angular cli using ng test –include option
  • Unable to run a Mango query
  • Unable to return response back to view in laravel from package
  • Unable to return object reference in std::optional
  • Unable to return NULL in a function that expects an integer return type
  • Unable to return correct change in JavaScript Cash Register
  • Unable to retrieve version information from Elasticsearch nodes. Request timed out
  • Unable to retrieve values from Axios Response data
  • Unable to retrieve dotenv JWT secret Error: secretOrPrivateKey must have a value
  • Unable to resolve your shell environment
  • Unable to resolve token for FCM while implementing Push notification for Xamarin
  • Unable to resolve the request yii
  • Unable to resolve service for type Swashbuckle.AspNetCore.Swagger.ISwaggerProvider
  • Unable to resolve service for type Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger