+bytearray and str speed

    
      
diff --git a/pythonetc/README.md b/pythonetc/README.md
index d4a2791..5274f60 100644
--- a/pythonetc/README.md
+++ b/pythonetc/README.md
@@ -74,6 +74,9 @@ More:
 1. ./lru-cache.md
 1. ./functools-cache.md
 1. ./tau.md
+1. ./str-append.md
+1. ./str-concat.md
+1. ./bytearray.md
 
 Out of order:
 
@@ -85,10 +88,8 @@ Out of order:
 Json indent
 Csv instead of xls
 Turtle
-Bytearray
 Subprocess and env vars
 Subprocess pipe
-Fnmatch?
 Unicode module
 String.Template
 String module consts
diff --git a/pythonetc/bytearray.md b/pythonetc/bytearray.md
new file mode 100644
index 0000000..0cef313
--- /dev/null
+++ b/pythonetc/bytearray.md
@@ -0,0 +1,20 @@
+Types `str` and `bytes` are immutable. As we learned in previous posts, `+` is optimized for `str` but sometimes you need a fairly mutable type. For such cases, there is `bytearray` type. It is a "hybrid" of `bytes` and `list`:
+
+```python
+b = bytearray(b'hello, ')
+b.extend(b'@pythonetc')
+b
+# bytearray(b'hello, @pythonetc')
+
+b.upper()
+# bytearray(b'HELLO, @PYTHONETC')
+```
+
+The type `bytearray` has all methods of both `bytes` and `list` except method `sort`:
+
+```python
+set(dir(bytearray)) ^ (set(dir(bytes)) | set(dir(list)))
+# {'__alloc__', '__class_getitem__', '__getnewargs__', '__reversed__', 'sort'}
+```
+
+If you're looking for reasons why there is no `bytearray.sort`, there is the only answer we found: [stackoverflow.com/a/22783330/8704691](https://stackoverflow.com/a/22783330/8704691).
diff --git a/pythonetc/str-append.md b/pythonetc/str-append.md
new file mode 100644
index 0000000..a1d4ed0
--- /dev/null
+++ b/pythonetc/str-append.md
@@ -0,0 +1,46 @@
+What is the fastest way to build a string from many substrings in a loop? In other words, how to concatenate fast when we don't know in advance how much strings we have? There are many discussions about it, and the common advice is that strings are immutable, so it's better to use a list and then `str.join` it. Let's not trust anyone and just check it.
+
+The straightforward solution:
+
+```python
+%%timeit
+s = ''
+for _ in range(10*8):
+  s += 'a'
+# 4.04 µs ± 256 ns per loop
+```
+
+Using lists:
+
+```python
+%%timeit
+a = []
+for _ in range(10*8):
+  a.append('a')
+''.join(a)
+# 4.06 µs ± 144 ns per loop
+```
+
+So, it's about the same. But we can go deeper. What about generator expressions?
+
+```python
+%%timeit
+''.join('a' for _ in range(10*8))
+# 3.56 µs ± 95.9 ns per loop
+```
+
+A bit faster. What if we use list comprehensions instead?
+
+```python
+%%timeit
+''.join(['a' for _ in range(10*8)])
+# 2.52 µs ± 42.1 ns per loop
+```
+
+Wow, this is 1.6x faster than what we had before. Can you make it faster?
+
+And there should be disclamer:
+
+1. Avoid [premature optimization](http://wiki.c2.com/?PrematureOptimization), value readability over performance when using a bit slower operation is tolerable.
+
+2. If you think that something is slow, prove it first. It can be different in your case.
diff --git a/pythonetc/str-concat.md b/pythonetc/str-concat.md
new file mode 100644
index 0000000..b3424bb
--- /dev/null
+++ b/pythonetc/str-concat.md
@@ -0,0 +1,67 @@
+Let's learn a bit more about strings performance. What if instead of unknown amount of strings we have only a few known variables?
+
+```python
+s1 = 'hello, '
+s2 = '@pythonetc'
+
+%timeit s1+s2
+# 56.7 ns ± 6.17 ns per loop
+
+%timeit ''.join([s1, s2])
+# 110 ns ± 6.09 ns per loop
+
+%timeit '{}{}'.format(s1, s2)
+# 63.3 ns ± 6.69 ns per loop
+
+%timeit f'{s1}{s2}'
+# 57 ns ± 5.43 ns per loop
+```
+
+No surprises here, `+` and f-strings are equaly good, `str.format` is quite close. But what if we have numbers instead?
+
+```python
+n1 = 123
+n2 = 456
+%timeit str(n1)+str(n2)
+# 374 ns ± 7.09 ns per loop
+
+%timeit '{}{}'.format(n1, n2)
+# 249 ns ± 4.73 ns per loop
+
+%timeit f'{n1}{n2}'
+# 208 ns ± 3.49 ns per loop
+```
+
+In this case, formatting is faster because it doesn't create intermediate strings. However, there is something else about f-strings. Let's measure how long it takes just to convert an `int` into a `str`:
+
+```python
+%timeit str(n1)
+# 138 ns ± 4.86 ns per loop
+
+%timeit '{}'.format(n1)
+# 148 ns ± 3.49 ns per loop
+
+%timeit format(n1, '')
+# 91.8 ns ± 6.12 ns per loop
+
+%timeit f'{n1}'
+# 63.8 ns ± 6.13 ns per loop
+```
+
+Wow, f-strings are twice faster than just `str`! This is because f-strings are part of the grammar but `str` is just a function that requires function-lookup machinery:
+
+```python
+import dis
+dis.dis("f'{n1}'")
+  1           0 LOAD_NAME                0 (n1)
+              2 FORMAT_VALUE             0
+              4 RETURN_VALUE
+
+dis.dis("str(n1)")
+  1           0 LOAD_NAME                0 (str)
+              2 LOAD_NAME                1 (n1)
+              4 CALL_FUNCTION            1
+              6 RETURN_VALUE
+```
+
+And once more, disclamer: readability is more important than performance until proven otherwise. Use your knowledge with caution :)